Skip to content

feat(adapters): UserPromptSubmit hook — inject approved knowledge before the model answers #425

Description

@plind-junior

the claude-code adapter wires two hook events today — SessionStart (a once-per-session status + recall digest) and PostToolUse (passive capture) — in adapters/claude-code/.claude/settings.json. there is no per-turn injection point. that means approved knowledge relevant to this prompt only reaches the model if the agent remembers to call kb.context / kb.search itself. an operator who just told the agent a fact, or asked a question the kb already answers, gets nothing back unless the agent volunteers a lookup — the highest-value moment (the turn boundary) is unhooked.

the reflex to surface that context already exists server-side: salience.record_query buffers recent queries and salience.attach_salience prefetches the top-k matched entities/claims into _meta.vouch_salience (#223). what's missing is the adapter wiring that feeds the prompt into it every turn and injects the result before the model thinks.

proposed surface

a new read-only cli command that the hook shells to:

vouch auto-context "<prompt text>" [--budget-chars N] [--format text|json]
  • runs entity-salience retrieval (salience + context.build_context_pack) over approved artifacts only, viewer-scoped, returns a small budgeted block.
  • writes nothing, proposes nothing, calls no propose_* / approve path — it is a viewport over the same reads kb.context already does.
  • latency-bounded: reuse the retrieval.reflex.* config (enabled, window, top_k) and the salience path rather than a cold FTS query per turn.

then add the hook to the adapter templates so it's installed at T4:

"UserPromptSubmit": [
  { "hooks": [ { "type": "command",
    "command": "vouch auto-context \"$CLAUDE_USER_PROMPT\" --format text" } ] }
]

the hook prints the block on stdout as additionalContext. it must be wired through the manifest-driven writer (install_adapter.py) and the T4 stanza in adapters/claude-code/install.yaml, then mirrored into adapters/cursor|codex|openclaw so all surfaces stay at parity (the invariant the openclaw manifest test already enforces for skills).

review gate & scope

read-only by construction — injection surfaces approved knowledge, it does not capture or write. nothing here can create or edit an artifact, so there is no proposal and no kb.approve step. because it reads the same viewer-scoped, approved corpus kb.context already exposes, it cannot leak across scopes any more than that path can (subject to the same filters). local-first: a shell command the host runs on the turn boundary, no daemon, no network.

acceptance criteria

  • vouch auto-context "<prompt>" runs read-only, surfaces approved-only context, writes nothing (no audit event, no proposal).
  • output is budget-capped and honors retrieval.reflex.* config; a kb with the reflex disabled emits an empty block.
  • the T4 install adds a UserPromptSubmit hook to .claude/settings.json via the manifest writer.
  • parity: the same hook lands in the cursor/codex/openclaw adapters (or is explicitly tiered where the host lacks the event).
  • tests/test_install_adapter.py asserts the hook is written at T4; a test covers the read-only invariant of auto-context.
  • make check green.

related: #223 (the salience reflex this consumes), #236 (kb.volunteer_context push channel), #54 (friendlier out of the box).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions