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
A conventional src/providers/<name>.ts factory receives only { invocation, signal }. It cannot read the request's identity axes (host, session, workspace), its lineage, or the mounted state/notices handles, because generated scopes run providers beforerunAgentRequest opens the request. So a provider cannot expose a derived view of shared app state — which is exactly what examples/worktree-proximity's agent-topology provider was meant to be. The example ships it as a permanent unavailable stub, and routes read topology from (await agent()).state instead.
For the plugin use-case (a topology()/peers() accessor apps can call from any route), a provider is the natural home: it is typed by typegen, mounted once per request, and mockable through the route-unit fixture seam. Today that home cannot see the data.
packages/agent-bundle/src/routes/provider-execution.ts:80-101 — executeProviders calls each factory with { invocation, signal } only.
packages/agent-bundle/src/build/entry-shell.ts:300-332 — routed CLI: requestBindings() (state + noticeLedger) exist at :311, providers execute at :312-316, then runAgentRequest({ …, noticeLedger, providers, state }) at :317-332. Same ordering for the rendered-route worker at :508-527.
docs/entry-conventions.md:85, 217 — public contract documents the factory as receiving { invocation, signal }.
examples/worktree-proximity/src/providers/agent-topology.ts:6-12 — returns { state: 'unavailable', reason: 'Topology snapshots are available only from the mounted request state handle; providers execute before that handle is mounted.' }.
examples/worktree-proximity/README.md:42-43, 61-64: "providers execute before request state is mounted, so this provider reports an honest unavailable result and routes read snapshots from (await agent()).state.read() instead."
Implementation sketch: in the generated scopes, run runAgentRequest with providers as a lazily-filled frozen record (or run the provider loop as the first step inside the request and freeze providers afterwards), so factories can call the same handles routes get. dispatch/publish stay off the provider surface (#95 stance). Providers that already destructure { invocation, signal } are unaffected.
Exposing the lineage tree itself (sibling issue, see "Related" comment); once both land, agent-topology becomes () => context.lineage plus the app's own intent state.
Acceptance
examples/worktree-proximity/src/providers/agent-topology.ts returns an available snapshot read from context.state.read() (or context.lineage) instead of the stub; src/mcp/coordinator/tools/status.tsx reads providers.agentTopology rather than withTopology(...); route-unit tests keep passing with the fixture seam (context.providers) and the cross-process suite packages/agent-bundle/tests/worktree-proximity-journeys.test.ts still shows exact-revision equality across processes. A framework test in entry-shell.test.ts proves a provider observes lineage and a read-only state on every generated surface.
Problem
A conventional
src/providers/<name>.tsfactory receives only{ invocation, signal }. It cannot read the request's identity axes (host,session,workspace), itslineage, or the mountedstate/noticeshandles, because generated scopes run providers beforerunAgentRequestopens the request. So a provider cannot expose a derived view of shared app state — which is exactly whatexamples/worktree-proximity'sagent-topologyprovider was meant to be. The example ships it as a permanentunavailablestub, and routes read topology from(await agent()).stateinstead.For the plugin use-case (a
topology()/peers()accessor apps can call from any route), a provider is the natural home: it is typed by typegen, mounted once per request, and mockable through the route-unit fixture seam. Today that home cannot see the data.Evidence (
main@284141958)packages/agent-bundle/src/routes/public.ts:95-102—AgentProviderContext = { invocation, signal };AgentProviderFactory = (context) => unknown.packages/agent-bundle/src/routes/provider-execution.ts:80-101—executeProviderscalls each factory with{ invocation, signal }only.packages/agent-bundle/src/build/entry-shell.ts:300-332— routed CLI:requestBindings()(state + noticeLedger) exist at:311, providers execute at:312-316, thenrunAgentRequest({ …, noticeLedger, providers, state })at:317-332. Same ordering for the rendered-route worker at:508-527.docs/entry-conventions.md:85, 217— public contract documents the factory as receiving{ invocation, signal }.examples/worktree-proximity/src/providers/agent-topology.ts:6-12— returns{ state: 'unavailable', reason: 'Topology snapshots are available only from the mounted request state handle; providers execute before that handle is mounted.' }.examples/worktree-proximity/README.md:42-43, 61-64: "providers execute before request state is mounted, so this provider reports an honest unavailable result and routes read snapshots from(await agent()).state.read()instead."resolve(request: AgentRequest); what landed (feat(routes): execute conventional context providers in generated request scopes (#95 remainder) #255) is narrower. Add typed Agent request context and pluggable context providers #95 also states providers "do not implicitly observe events or mutate durable state" — this proposal keeps that: read access only.Proposed shape (small)
Widen
AgentProviderContextwith the read-only parts of the request, and open the request before the provider loop:Implementation sketch: in the generated scopes, run
runAgentRequestwithprovidersas a lazily-filled frozen record (or run the provider loop as the first step inside the request and freezeprovidersafterwards), so factories can call the same handles routes get.dispatch/publishstay off the provider surface (#95 stance). Providers that already destructure{ invocation, signal }are unaffected.Out of scope / related
agent-topologybecomes() => context.lineageplus the app's own intent state.Acceptance
examples/worktree-proximity/src/providers/agent-topology.tsreturns anavailablesnapshot read fromcontext.state.read()(orcontext.lineage) instead of the stub;src/mcp/coordinator/tools/status.tsxreadsproviders.agentTopologyrather thanwithTopology(...); route-unit tests keep passing with the fixture seam (context.providers) and the cross-process suitepackages/agent-bundle/tests/worktree-proximity-journeys.test.tsstill shows exact-revision equality across processes. A framework test inentry-shell.test.tsproves a provider observeslineageand a read-onlystateon every generated surface.