Need
An app process that wants an agent turn without a browser — a scheduled automation, a resumed approval, a typed workflow step — has no supported door into the configured agent-chat runtime. The configured handler is a closure inside createAgentChatPlugin, and _process-run only rehydrates a run row a foreground POST inserted. Today apps either fake an HTTP request (minting a session, loopback POST, draining the SSE) or reimplement the run lifecycle.
What we run (as a patch)
createAgentChatPlugin({ onServerRuntimeReady(runtime) { … } }) hands the app an in-process runtime:
interface AgentChatServerRuntime {
submit(input: TrustedSubmission): Promise<SubmissionReceipt>; // idempotent by key
inspect(receipt): Promise<ScopedTurnState>; // running | stale | completed | errored | aborted | missing
cancel(receipt): Promise<void>;
ready(): { ready: boolean; reason: "app_id_missing" | "a2a_secret_missing" | null };
}
submit checks the thread exists, its scope matches, and the principal has editor access (resolveThreadAccess); resolves the turn (a new one, or the paused turn an approvedToolCalls replay continues via resolveAgentToolApprovalTurnId); records an idempotency receipt (app, principal, key) → (thread, turn, run) where a changed input hash under the same key is a conflict; records the turn's principal; admits the turn through Core's own tryClaimRunSlot (run row + dispatch payload + continuation order under the advisory lock); and fires the same signed self-dispatch a browser turn does. From there the existing worker, recovery sweep and completion hooks run unchanged. Nothing is reachable over HTTP — the payload carries a __serverSubmission marker that the handler honours only on the verified worker path and strips from any HTTP body.
It also supports exact action mode: submit({ action: { name, validatedInput } }) runs one validated call through the same guarded runtime as an approved replay (schema validation, approval gate + grant, journal, redaction), with no model.
The worker resolves the acting member from the recorded turn principal (agent_turn_initiators), falling back to the thread owner for legacy runs.
Ask
Would you take this as a supported API? We can open a PR from our implementation (it's ~300 lines over run-store/turn-initiators, plus the plugin option and exports), or adapt it to a shape you prefer.
Need
An app process that wants an agent turn without a browser — a scheduled automation, a resumed approval, a typed workflow step — has no supported door into the configured agent-chat runtime. The configured handler is a closure inside
createAgentChatPlugin, and_process-runonly rehydrates a run row a foreground POST inserted. Today apps either fake an HTTP request (minting a session, loopback POST, draining the SSE) or reimplement the run lifecycle.What we run (as a patch)
createAgentChatPlugin({ onServerRuntimeReady(runtime) { … } })hands the app an in-process runtime:submitchecks the thread exists, its scope matches, and the principal has editor access (resolveThreadAccess); resolves the turn (a new one, or the paused turn anapprovedToolCallsreplay continues viaresolveAgentToolApprovalTurnId); records an idempotency receipt(app, principal, key) → (thread, turn, run)where a changed input hash under the same key is a conflict; records the turn's principal; admits the turn through Core's owntryClaimRunSlot(run row + dispatch payload + continuation order under the advisory lock); and fires the same signed self-dispatch a browser turn does. From there the existing worker, recovery sweep and completion hooks run unchanged. Nothing is reachable over HTTP — the payload carries a__serverSubmissionmarker that the handler honours only on the verified worker path and strips from any HTTP body.It also supports exact action mode:
submit({ action: { name, validatedInput } })runs one validated call through the same guarded runtime as an approved replay (schema validation, approval gate + grant, journal, redaction), with no model.The worker resolves the acting member from the recorded turn principal (
agent_turn_initiators), falling back to the thread owner for legacy runs.Ask
Would you take this as a supported API? We can open a PR from our implementation (it's ~300 lines over
run-store/turn-initiators, plus the plugin option and exports), or adapt it to a shape you prefer.