You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Generated event request scopes never mount an actor principal, so every event route observes (await agent()).actor as unavailable with the default not-provided reason. This was parked on the closed #233 (state-kernel remainder) and #223 (request-context exposure) and has had no tracker since the G1 closed-issue audit (#366) confirmed the gap is still open. This issue is the tracker. It proposes no fabricated principal; it asks for a typed, evidence-backed unavailable surface and names the one host field that could make actor derivable today.
Current behavior
docs/entry-conventions.md (Handler request context, L166-173): "Bare stdio supplies neither a session id nor HTTP actor authentication, so those axes remain honestly unavailable. Generated event scopes currently mount no actor principal, so event routes observe actor as unavailable rather than receiving a fabricated value."packages/agent-bundle/src/routes/public.ts:62 repeats the claim on AgentEventRouteProps.
Shared event runtime, packages/agent-bundle/src/mcp-server-runtime.ts (createGeneratedRouteMcpServer event dispatch, ~L499-510): runAgentRequest({ host: available({ name: target }, 'native'), invocation, session?, signal, workspace? }) — no actor key, so the runtime's default unavailable('not-provided') applies.
Standalone event wrapper, packages/agent-bundle/src/adapters/hook-contract.ts (~L632-637): derives session from session_id/conversation_id and workspace from cwd; mounts no actor.
Contrast: generated MCP tool scopes do mount an actor when the transport supplies one — requestIdentity in mcp-server-runtime.ts (~L104-119) maps http.authInfo.clientId to available({ id }, 'native'); bare stdio yields unavailable('not-provided').
The runtime already distinguishes four reasons (packages/rsc-runtime/src/agent-request.ts:51-55): not-provided | unsupported-surface | host-omitted | unauthenticated. Event scopes use none of them deliberately; the observed reason is the default.
Host contracts checked (pinned files)
No pinned host hook contract exposes a human/account principal in its event envelope, with one exception (Cursor workspaceOpen).
Host (pinned revision)
Pinned evidence
Envelope identity fields
Principal?
Claude Code 2.1.250 (src/adapters/schemas/claude/PROVENANCE.json, observedCliVersion: 2.1.250)
schemas/claude/hooks.schema.json (hooks.json configuration document — command/matcher/timeout only); live-capture evidence in adapters/capabilities/claude-2.1.250.json (hooks evidence list) and scrubbed envelopes tests/fixtures/events/claude-*.json
Only workspaceOpen.projection.ts:109-113 already validates user_email as `string
Also checked: the pinned hooks.schema.json for all three hosts describe the hooks configuration document (which commands run for which matcher), so by construction none of them can carry a principal — the envelope is the only channel, and the envelope evidence above is what rules it out.
What would make actor derivable
Host-supplied principal in the envelope (source: 'native'). Cursor's workspaceOpen.user_email is the only pinned instance. Claude Code and Codex publish none at the pinned revisions; a future common-envelope field would be adopted by re-pinning the schema/capability tables under the normal adapter chore.
Receipt-derived principal (source: 'receipt', already an ObservedSource). A host login receipt (for example the signed-in account the CLI reports) read at event time. Not proposed here: it is a host-install/discovery concern, it crosses the pinned contract, and it must never be read without an explicit capability grant.
Event scopes mount actorexplicitly, with the reason that matches the pinned evidence, instead of relying on the runtime default:
unavailable('host-omitted') for every Claude Code and Codex event, and for every Cursor event other than workspaceOpen — the pinned envelope has no principal field.
Cursor workspaceOpen: available({ id: user_email }, 'native') when user_email is a non-empty string; unavailable('host-omitted') when it is null/absent.
Keep not-provided for the harness/module-direct case only (no host in play).
Both the shared event runtime (mcp-server-runtime.ts) and the standalone wrapper (hook-contract.ts) must agree; a route-unit test per host fixture pins the reason, and docs/entry-conventions.md L166-173 plus routes/public.ts:62 change from "currently unavailable" to the per-host table above.
Non-goals: no synthesized or guessed principal (no USER/os.userInfo() fallback, no transcript parsing), no new AgentContextUnavailableReason unless the pinned contracts require one.
Acceptance
Event scopes never observe the default not-provided; the reason is one of the typed reasons above and is pinned per host fixture.
Cursor workspaceOpen exposes user_email as actor when present, per the pinned capability table.
Docs and public.ts describe the per-host derivation rather than a blanket "unavailable".
Summary
Generated event request scopes never mount an actor principal, so every event route observes
(await agent()).actorasunavailablewith the defaultnot-providedreason. This was parked on the closed #233 (state-kernel remainder) and #223 (request-context exposure) and has had no tracker since the G1 closed-issue audit (#366) confirmed the gap is still open. This issue is the tracker. It proposes no fabricated principal; it asks for a typed, evidence-backedunavailablesurface and names the one host field that could makeactorderivable today.Current behavior
docs/entry-conventions.md(Handler request context, L166-173): "Bare stdio supplies neither a session id nor HTTP actor authentication, so those axes remain honestly unavailable. Generated event scopes currently mount no actor principal, so event routes observe actor as unavailable rather than receiving a fabricated value."packages/agent-bundle/src/routes/public.ts:62repeats the claim onAgentEventRouteProps.packages/agent-bundle/src/mcp-server-runtime.ts(createGeneratedRouteMcpServerevent dispatch, ~L499-510):runAgentRequest({ host: available({ name: target }, 'native'), invocation, session?, signal, workspace? })— noactorkey, so the runtime's defaultunavailable('not-provided')applies.packages/agent-bundle/src/adapters/hook-contract.ts(~L632-637): derivessessionfromsession_id/conversation_idandworkspacefromcwd; mounts noactor.requestIdentityinmcp-server-runtime.ts(~L104-119) mapshttp.authInfo.clientIdtoavailable({ id }, 'native'); bare stdio yieldsunavailable('not-provided').packages/rsc-runtime/src/agent-request.ts:51-55):not-provided | unsupported-surface | host-omitted | unauthenticated. Event scopes use none of them deliberately; the observed reason is the default.Host contracts checked (pinned files)
No pinned host hook contract exposes a human/account principal in its event envelope, with one exception (Cursor
workspaceOpen).src/adapters/schemas/claude/PROVENANCE.json,observedCliVersion: 2.1.250)schemas/claude/hooks.schema.json(hooks.json configuration document — command/matcher/timeout only); live-capture evidence inadapters/capabilities/claude-2.1.250.json(hooks evidence list) and scrubbed envelopestests/fixtures/events/claude-*.jsonsession_id,transcript_path,cwd,hook_event_name,permission_mode,prompt_id; Subagent* addagent_id,agent_typeagent_id/agent_typename a subagent, not a user.schemas/codex/PROVENANCE.json, byte-pinned fromrust-v0.147.0)schemas/codex/generated/*.command.input.schema.json(e.g.user-prompt-submit:agent_id, agent_type, cwd, hook_event_name, model, permission_mode, prompt, session_id, transcript_path, turn_id;session-end:cwd, session_id, transcript_path, …)schemas/cursor/PROVENANCE.json)schemas/cursor/hooks.schema.json(configuration document, vendored); envelope evidence inadapters/capabilities/cursor-2026-08-28.json(workspaceOpen entries) andsrc/events/projection.ts:96-118conversation_id,generation_id,model,session_id,transcript_path,hook_event_name,cursor_version;workspaceOpen(sessionless):hook_event_name,cursor_version,workspace_roots,user_emailworkspaceOpen.projection.ts:109-113already validatesuser_emailas `stringAlso checked: the pinned
hooks.schema.jsonfor all three hosts describe the hooks configuration document (which commands run for which matcher), so by construction none of them can carry a principal — the envelope is the only channel, and the envelope evidence above is what rules it out.What would make
actorderivablesource: 'native'). Cursor'sworkspaceOpen.user_emailis the only pinned instance. Claude Code and Codex publish none at the pinned revisions; a future common-envelope field would be adopted by re-pinning the schema/capability tables under the normal adapter chore.source: 'receipt', already anObservedSource). A host login receipt (for example the signed-in account the CLI reports) read at event time. Not proposed here: it is a host-install/discovery concern, it crosses the pinned contract, and it must never be read without an explicit capability grant.src/providers/*provider (Mount request context providers for routed CLI and rendered scripts #313/fix(cli): mount request context providers for plain routed CLI commands (#313 audit) #366). Already possible today for projects that know their own identity source; it lands underproviders.<key>, notactor, and stays out of the framework's identity axis on purpose.authInfo— already derivable for tool scopes over HTTP transports; not applicable to event scopes, which are host-spawned processes.Proposed typed
unavailablesurface (not implemented here)actorexplicitly, with the reason that matches the pinned evidence, instead of relying on the runtime default:unavailable('host-omitted')for every Claude Code and Codex event, and for every Cursor event other thanworkspaceOpen— the pinned envelope has no principal field.workspaceOpen:available({ id: user_email }, 'native')whenuser_emailis a non-empty string;unavailable('host-omitted')when it isnull/absent.not-providedfor the harness/module-direct case only (no host in play).mcp-server-runtime.ts) and the standalone wrapper (hook-contract.ts) must agree; a route-unit test per host fixture pins the reason, anddocs/entry-conventions.mdL166-173 plusroutes/public.ts:62change from "currently unavailable" to the per-host table above.USER/os.userInfo()fallback, no transcript parsing), no newAgentContextUnavailableReasonunless the pinned contracts require one.Acceptance
not-provided; the reason is one of the typed reasons above and is pinned per host fixture.workspaceOpenexposesuser_emailasactorwhen present, per the pinned capability table.public.tsdescribe the per-host derivation rather than a blanket "unavailable".Refs #233, #223, #313, #366.