Summary
Wave 1 (#113 / parent #95) installs a full AgentRequestContext in runAgentRequest and exposes await agent() from the same ALS store, but RSC operation handlers still receive only { signal } on operation.execute. MCP wiring forwards native sessionId into the request store yet never supplies host identity; CLI marks host unavailable('unsupported-surface'). Plugin authors therefore cannot rely on transport-observed host/session in handler code or in structured audit fields without re-declaring identity in tool JSON.
This is a focused wiring/API slice on top of #95 (typed request context). It should be proven by the generated harness in #103, consumed by Workbench/event surfaces in #105, and guarded against source→installed drift in #218.
Evidence (current main-line behavior)
RscOperationContext exposes only signal:
// packages/rsc-runtime/src/operation.ts:6-8
export interface RscOperationContext {
readonly signal: AbortSignal;
}
AgentRequestContext already carries observed host/session/actor/workspace:
// packages/rsc-runtime/src/agent-request.ts:128-137
export interface AgentRequestContext {
readonly invocation: AgentInvocation;
readonly host: Observed<AgentHostIdentity>;
readonly session: Observed<AgentSessionIdentity>;
readonly actor: Observed<AgentActorIdentity>;
readonly workspace: Observed<AgentWorkspaceIdentity>;
readonly capabilities: AgentRequestCapabilities;
readonly progress: AgentProgressReporter;
readonly signal: AbortSignal;
readonly services: AgentServiceRegistry;
readonly providers: AgentProviderValues;
// ...
}
MCP: session from native context, but execute gets { signal } only — no host:
// packages/rsc-runtime/src/mcp-server.ts (tool handler)
async (input, context) => runAgentRequest({
...(context.http?.authInfo?.clientId === undefined
? {}
: { actor: available({ id: context.http.authInfo.clientId }, 'native') }),
invocation: { kind: 'tool', operationId: operation.id, surface: mcp.name },
...(typeof context.sessionId === 'string' && context.sessionId.trim() !== ''
? { session: available({ sessionId: context.sessionId }, 'native') }
: {}),
signal: context.mcpReq.signal,
}, async () => {
const result = await operation.execute(input, { signal: context.mcpReq.signal });
// ...
});
CLI: host explicitly unavailable:
// packages/rsc-runtime/src/cli.ts:44-45 (representative)
host: unavailable('unsupported-surface'),
// ...
async () => operation.execute(cli.parse(commandArguments), { signal })
Real-world impact (cargo-conductor)
In cargo-conductor, ledger rows cc-800 and cc-803 recorded session plan40-stage3-sol but host NULL, rendering UI as ? · plan40-stage3-sol. The conductor_request tool input had to ask agents to manually supply host/session in JSON. Transport already knew session (MCP native); host should be observed from adapter/plugin/client metadata where knowable. Plugins should not force callers to re-declare transport identity in business input.
Requested design
-
Extend RscOperationContext with the request-bound AgentRequestContext, or at minimum the observed host / session / actor / workspace / invocation fields, sourced from the same ALS store used by agent() during runAgentRequest (no second, divergent snapshot).
-
Populate MCP observed host from host adapter / plugin manifest / MCP client metadata where knowable (e.g. Cursor, Claude Code, Codex, Workbench dev proxy). Preserve unavailable with typed reason when not knowable — never fabricate placeholders.
-
CLI, Workbench, and semantic event routes should receive honest observed identities (derived or native as appropriate), not blanket unsupported-surface where the framework can observe more.
-
Generated test harness acceptance (#103):
- Native MCP session (and host when adapter provides it) reaches
operation.execute context (or await agent() parity).
- Explicit user/tool input cannot spoof or override transport-observed identity unless route policy explicitly allows override.
unavailable remains typed with provenance/reason in assertions.
-
API shape: prefer a principal/context argument separate from operation business input (second parameter to execute, or documented await agent()) rather than auto-injecting host/session fields into the Zod input schema — keeps validation boundaries clear and prevents agents from forging transport identity via JSON.
Non-goals
- Replacing #95 provider discovery/typegen work.
- Inferring parent/subagent topology without host contract evidence (#95 non-goals).
Acceptance criteria
Stack links
Summary
Wave 1 (#113 / parent #95) installs a full
AgentRequestContextinrunAgentRequestand exposesawait agent()from the same ALS store, but RSC operation handlers still receive only{ signal }onoperation.execute. MCP wiring forwards nativesessionIdinto the request store yet never supplies host identity; CLI marks hostunavailable('unsupported-surface'). Plugin authors therefore cannot rely on transport-observed host/session in handler code or in structured audit fields without re-declaring identity in tool JSON.This is a focused wiring/API slice on top of #95 (typed request context). It should be proven by the generated harness in #103, consumed by Workbench/event surfaces in #105, and guarded against source→installed drift in #218.
Evidence (current
main-line behavior)RscOperationContextexposes onlysignal:AgentRequestContextalready carries observed host/session/actor/workspace:MCP: session from native context, but
executegets{ signal }only — no host:CLI: host explicitly unavailable:
Real-world impact (cargo-conductor)
In cargo-conductor, ledger rows cc-800 and cc-803 recorded session
plan40-stage3-solbut host NULL, rendering UI as? · plan40-stage3-sol. Theconductor_requesttool input had to ask agents to manually supply host/session in JSON. Transport already knew session (MCP native); host should be observed from adapter/plugin/client metadata where knowable. Plugins should not force callers to re-declare transport identity in business input.Requested design
Extend
RscOperationContextwith the request-boundAgentRequestContext, or at minimum the observed host / session / actor / workspace / invocation fields, sourced from the same ALS store used byagent()duringrunAgentRequest(no second, divergent snapshot).Populate MCP observed host from host adapter / plugin manifest / MCP client metadata where knowable (e.g. Cursor, Claude Code, Codex, Workbench dev proxy). Preserve
unavailablewith typed reason when not knowable — never fabricate placeholders.CLI, Workbench, and semantic event routes should receive honest observed identities (derived or native as appropriate), not blanket
unsupported-surfacewhere the framework can observe more.Generated test harness acceptance (#103):
operation.executecontext (orawait agent()parity).unavailableremains typed with provenance/reason in assertions.API shape: prefer a principal/context argument separate from operation business input (second parameter to
execute, or documentedawait agent()) rather than auto-injecting host/session fields into the Zod input schema — keeps validation boundaries clear and prevents agents from forging transport identity via JSON.Non-goals
Acceptance criteria
operation.execute(input, ctx)(or equivalent) exposes the same observed identities available viaawait agent()for that invocation.unavailable.Stack links