Imported from generic-automation-and-it/smooth-llm-imposter (
.docs/hlds/010-who-message-introspection/AGENTS.md). Install upstream withnpx skills add generic-automation-and-it/smooth-llm-imposter --skill 010-who-message-introspection. Copyright stays with the author.
AGENTS.md - Who-Message Introspection
AI Context: HLD for the switch-family feature (HLD 010). IMPLEMENTED — --who?, --newsession, and the in-memory translation dictionary are live. Updated: 2026-07-25
Implementation status
Both switches are implemented:
--who?— routing probe:Imposter: <inbound> → <target> (auth: <scheme>, session: <id>)--newsession— mints a synthetic session id and stores the caller→synthetic mapping in the in-memory translation dictionary
The ISessionTranslationDictionary is a process-lifetime ConcurrentDictionary registered as a DI singleton. On the forward path, when the plan's session identity matches a dictionary key, the synthetic id replaces the caller id before stamping.
AI agents: The switch table in WhoMessageResponder.Switches is the source of truth for trigger literals. Adding a new switch is a localized change to the responder's switch table — not a new LADR, not a new config node.
TL;DR
A request whose last user message is exactly --who? or --newsession (trimmed, case-sensitive, non-streaming) short-circuits the forward path and returns a dialect-shaped synthetic reply. The in-memory translation dictionary (minted by --newsession) translates caller-supplied session ids to synthetic ids on the forward path. Intent in README.md, decisions in ladrs/, quality spec in nfrs/.
Non-Negotiables
- Do not make routing decisions based on
messagescontent anywhere except the switch responder. (Body-shape transformation by HLD 004 / HLD 006 transformers is fine; routing-shape inspection ofmessagesis the switch responder's exclusive job.) The proxy's transparency property (HLD 001) depends on routing decisions staying opaque to message content; this HLD carves out exactly one sanctioned inspection point, and a second one added silently elsewhere would break the transparency invariant. - Do not synthesize SSE. Streaming requests forward unchanged even when the last
user message is a registered switch (
--who?or--newsession, LADR-05). Adding streaming synthesis duplicates logic that already lives in the real transformers and drifts with every upstream format change. - Do not expose secrets, credentials, base URLs, or provider registry keys in the
reply. The content text carries only: inbound model, resolved target (or
passthrough), auth scheme name, and (for--who?) the persisted session id orsession: null(NFR-03, LADR-03). The synthetic id from--newsessionis also non-secret — it is generated by the proxy, not a credential. - Reuse
ImposterRouter.DescribeAuth(promoted tointernal static) for the auth string. Re-deriving the scheme precedence locally will drift from the forwarder's actual header. - Triggers are exact-match
--who?or--newsessionafter trim, case-sensitive, last user message only. Do not add regex, case-insensitive, or "any message in history" variants (LADR-02). Adding a new switch is a localized change to the responder's switch table (WhoMessageResponder.Switches) — not a new LADR, not a new config node. - Feature is gated; default ON. Do not hardcode enable or disable. The
Imposter:WhoMessage:Enabledboolean (envIMPOSTER_WHO_MESSAGE_ENABLED) must be readable at request time.falsemust skip BOTH the switch short-circuit AND the in-memory translation dictionary on the forward path (LADR-04, LADR-06) — not just the reply. - Translation dictionary is process-lifetime; no TTL, no eviction, no clear.
Adding an eviction policy, a TTL, or a
Clear()API contradicts the user's contract for this feature (LADR-06, NFR-04). The dictionary is in-memoryConcurrentDictionaryregistered as a DI singleton; do not add aRemove/Evict/Clearmethod on its public surface. A process restart loses all entries — that is intentional, documented in NFR-04.
Architecture Decisions
| LADR | Decision | Why it matters |
|---|---|---|
| LADR-01 | Short-circuit inside the proxy, seam between PlanAsync and SendAsync |
A separate endpoint would duplicate the resolver + auth logic and drift from the real forward path. |
| LADR-02 | Exact --who? or --newsession match on the last user message |
Regex / header / configurable triggers all raise false-positive or adoption-cost problems. |
| LADR-03 | Dialect-shaped chat envelope (shared by both switches) | Bare text forces out-of-band client branching; SDK clients parse the reply unchanged. |
| LADR-04 | Default-ON, env-overridable, shared by both switches AND the dictionary | Adoption cliff for a zero-cost feature if opt-in; toggle gates the whole family. |
| LADR-05 | Streaming requests pass through | SSE synthesis is high-drift, low-value; streaming callers re-issue as non-streaming. |
| LADR-06 | In-memory ConcurrentDictionary translates caller-supplied session ids to stored override ids on the forward path |
Lets upstreams group by a proxy-owned id without forcing the caller to coordinate a long-lived secret; process-lifetime per the user's contract. |
Key Behaviors
- Seam location. The short-circuit sits after
router.PlanAsync(soRoutePlanis available) and beforeforwarder.SendAsync(so no outbound call fires on match). The translation dictionary is consulted at the same seam but on the non-match path: after the plan produces aSessionIdentity, before the transformer stamps it on the outbound request. - Trigger is body-only.
stream:truein the body disables the short-circuit regardless of the message content. Header-only signals (Accept: text/event-stream) are not consulted — the forwarder keys off the body too. - Non-text last user content → no match. A last user message built from image or tool parts does not fire either switch. Multi-part text content is supported: an array of text parts matches when the concatenated trimmed value equals the trigger literal. The LADR-02 follow-up to narrow to bare-string only is intentionally deferred.
DescribeAuthreturn value is the auth-scheme vocabulary. The same tokens the log emits (Bearer/ApiKey/caller-passthrough/none) appear in the--who?reply. Do not invent a parallel vocabulary.--newsessionrequires a caller-supplied session id (header or body field per the HLD 009 resolution order). A--newsessionrequest with no caller-supplied id does not match — the responder returns no match and the request forwards normally. The translation step on the forward path has no fallback.- The dictionary only rewrites
SessionIdentity.ValueafterPlanAsyncreturns; callers that stamp the body via the transformer (e.g.OpenAiRequestTransformer) must either (a) consult the dictionary before stamping, or (b) accept that the bodysession_idfield carries the caller's id and rely on the upstream provider to honour thex-opencode-sessionheader.
Quality Constraints
See nfrs/ for measurable targets. The four that change how code is written:
- NFR-01 (Transparency): non-match + feature-disabled paths must be byte-identical to pre-HLD. The switch responder and the translation dictionary are both skipped when the toggle is off.
- NFR-03 (No secret leakage): the switch responder's only credential dependency
is the scheme-name string from
DescribeAuth; it must not readSecretorCredentialOverride.Secret. The synthetic id from--newsessionis not a secret but is also not a credential. - NFR-04 (Process-lifetime dictionary): the translation dictionary does not
evict; a second
--newsessionwith the same caller id returns the same synthetic id (no overwrite). The dictionary's public surface has noRemove/Evict/Clearmethod — the contract is enforced at the type level, not just by convention. - L0/L2 test coverage: every switch literal (
--who?,--newsession) has a L0 unit test inWhoMessageResponderTestsand at least one L2 integration test inWhoMessageIntegrationTests. The barewho?string is covered as a no-match negative case. The translation step has at least one L2 test proving that the outbound request carries the synthetic id, not the caller id.
Changelog
| Date | Change | Ref |
|---|---|---|
| 2026-07-25 | Initial HLD AGENTS.md — 5 LADRs, 3 NFRs, 1 diagram file (3 mermaid blocks). | — |
| 2026-07-25 | Extended design (NOT YET IMPLEMENTED): proposed trigger is --who? (was who?); proposed --newsession switch + in-memory translation dictionary. HLD is in design — implementation lands in a follow-up commit. LADR-06 and NFR-04 are Draft. AI agents: do not modify the live who? trigger or the IWhoMessageResponder contract on the basis of this AGENTS.md alone. Breaking change: renaming the live who? trigger to --who?. |
— |
| 2026-07-25 | Implemented: --who? and --newsession switches + ISessionTranslationDictionary + forward-path translation seam. LADRs/NFRs → Accepted; HLD → Completed. Diagnostic logging added to WhoMessageResponder and RoutingEndpoints for non-match reasons. |
— |