Skip to content

feat(runtime): streamAgentTurn — one run-turn event contract over box/executor/chat backends#454

Merged
drewstone merged 1 commit into
mainfrom
feat/stream-agent-turn
Jul 3, 2026
Merged

feat(runtime): streamAgentTurn — one run-turn event contract over box/executor/chat backends#454
drewstone merged 1 commit into
mainfrom
feat/stream-agent-turn

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Closes #452

What

streamAgentTurn(backend, prompt, { signal?, timeoutMs? }) → AsyncGenerator<RuntimeStreamEvent> — ONE run-a-turn primitive over the three execution substrates, plus collectAgentTurn(stream) to drain it into { finalText, usage, events, status, error? }. Exported from the ./loops barrel. Version 0.85.0.

AgentTurnBackend is a closed discriminated union:

  • { kind: 'box', box } — one SandboxInstance.streamPrompt call, projected through the EXISTING mapSandboxEvent / extractLlmCallEvent (sandbox-events.ts). No re-mapping.
  • { kind: 'executor', factory } — a one-shot Executor (cli-bridge / router / BYO) adapted through the EXISTING inlineSandboxClient (the one executor→box adapter), then driven by the same box path. Settle/teardown lifecycle stays in that adapter.
  • { kind: 'chat', backend } — one turn through an in-process AgentExecutionBackend (resolveAgentBackend output), normalized by the EXISTING normalizeBackendStreamEvent.

Stream envelope: backend_start → incremental events → (backend_error on failure) → final. The terminal final event is guaranteed on every non-thrown path and carries text (final text) + metadata.tokenUsage {input, output} / metadata.costUsd? / metadata.model?. Caller abort ends with status: 'aborted'; a blown timeoutMs deadline with status: 'failed' — cancellation stays distinguishable from a missed deadline.

Reused vs new

Reused (the point of the issue): mapSandboxEvent + extractLlmCallEvent, inlineSandboxClient, normalizeBackendStreamEvent, newRuntimeSession, scoreKnowledgeReadiness, BackendTransportError → typed BackendErrorDetail. New code is thin adapters + the terminal-event normalization (one accumulator fold) — no new stream parser.

Supporting root fix: inlineSandboxClient's pseudo-box streamPrompt now accepts { signal } and chains it into the executor's spawn signal (it previously ignored the second argument every real SandboxInstance honors), so abort reaches exec.execute — for this primitive AND for runLoop driving inline boxes.

Honest-usage detail: mapSandboxEvent stamps agentRunName as the model label on events that carried no model; the fold excludes that run label from usage.model so the terminal usage never reports a fabricated model.

Tests (offline, 9 new)

Per backend kind — box via inProcessSandboxClient, executor via a stub ExecutorFactory, chat via a stub AgentExecutionBackend:

  • incremental events surface in order before the terminal event
  • terminal final carries tokenUsage / costUsd / model
  • collectAgentTurn round-trips the summary; throws on a stream with no terminal event
  • abort mid-stream → status: 'aborted', executor teardown observed, partial text preserved
  • timeoutMs expiry → status: 'failed' with the deadline in the error
  • a throwing box surfaces backend_error + final failed in-band (generator never throws)

Gate: lint clean, typecheck clean, build clean, full suite 1221 passed / 2 pre-existing skips. docs:api + docs:freshness green (canonical-api decision-table row added).

Motivating consumer: physim#70 — its four hand-written provider-stream→AgentEvent generators become thin mappers over this primitive.

@tangletools tangletools left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Auto-approved drewstone PR — 0711ca33

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-03T07:12:54Z

@drewstone drewstone merged commit b6cdb55 into main Jul 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Run-turn primitive: streamAgentTurn — one event-stream contract over box/bridge/router/in-process

2 participants