feat(0.16.0): yank durable substrate, add AgentExecutionHandle contract#34
Merged
Merged
Conversation
Long-running execution durability lives in the substrate (sandbox SDK + orchestrator already buffer the stream by executionId and replay strictly after lastEventId). The Worker-hosted SessionSupervisorDO, runSupervisedTurn, runDurableTurn, runDurable, runOnWorkflowStep, DurableRunStore (+ D1 / FS / in-memory impls), schema, and supervisor adapter all competed with primitives that already exist below — and adoption proved it: SessionSupervisorDO had zero callers, runDurable / runOnWorkflowStep had zero callers, runSupervisedTurn had one (gtm-agent). Replaced with two surfaces: - AgentExecutionHandle + deriveExecutionId — the typed pointer products persist so a client retry lands on the same substrate execution. - ChatTurnEngine / chatTurnEngine — the framework-neutral chat-turn lifecycle (NDJSON, session.run.* envelope, persist / post-process / trace-flush hook ordering). No longer hosts execution state. Build: 179 tests pass (was 251 — store-matrix tests removed). pnpm-lock drops 110 packages (workerd test pool, better-sqlite3, wrangler). Consumers (tax / legal / creative / gtm) bump on a separate PR; they only use DurableChatTurnEngine.runTurn(...) and D1DurableRunStore — both adopted shallow, easy migration.
…ending) @tangle-network/sandbox@0.1.2 PromptOptions does not yet surface executionId / lastEventId. In-call reconnect is automatic; cross-process reconnect requires either bypassing the SDK (raw HTTP + X-Execution-ID header) or a future SDK release. Doc clarified to reflect actual state. Also: fix stale 'durable substrate' framing in src/index.ts and README.md headline.
Round-1 audit critiques applied: - ChatTurnEngine class + chatTurnEngine singleton → handleChatTurn function. The class wrapped a single stateless method; the singleton was the singleton-of-a-stateless-class antipattern. One symbol now. - Renamed to `handleChatTurn` (not `runChatTurn`) to disambiguate from the existing `chat-turn.ts:runChatTurn` sandbox-stream primitive — different concern, different name. - Dropped `AgentExecutionHandle` interface — only the executionId string is used at call sites; the wrapper type bought nothing. - Dropped `AgentExecutionHandle.lastEventId` — the public sandbox SDK does not surface it, and no consumer used it. Adding back when SDK exposes it. - Dropped `ReconnectableAgentStream<TEvent>` — exported with zero callers, premature abstraction. - Dropped `ChatTurnHooks.accumulate` — unused by all consumers; the producer's `finalText()` is the single source of truth. - Default `log` to `console.error` so swallowed hook errors don't vanish silently when no logger is wired. durable/ source now: chat-engine.ts (handleChatTurn), execution-handle.ts (deriveExecutionId only), index.ts (exports). README + concepts + the chat-handler example all simplified to match. 179 tests pass.
831b9f4 to
13a1283
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Yanks the Worker-hosted durable substrate (SessionSupervisorDO, runSupervisedTurn, runDurableTurn, runDurable, runOnWorkflowStep, DurableRunStore + D1 / FS / in-memory impls, schema, identity helpers) and replaces it with a thin contract over what the substrate already does:
AgentExecutionHandle+deriveExecutionId— typed pointer products persist so a client retry lands on the same substrate execution. The sandbox SDK + orchestrator already buffer byexecutionId, replay strictly afterlastEventId, and never spawn a duplicate.ChatTurnEngine/chatTurnEngine— framework-neutral chat-turn lifecycle: NDJSON framing,session.run.*envelope, persist / post-process / trace-flush hook ordering. No longer hosts execution state.Why now
Cross-repo audit showed the supervisor surface competed with primitives that already live in
@tangle-network/sandbox+ orchestrator:box.streamPrompt({ executionId, lastEventId })already reconnects + replays./agents/run/streamalready handlesX-Execution-ID/Last-Event-IDand never spawns a duplicate.execution-buffer.tskeyed by{sidecarId}:{executionId}already buffers events.session-gatewayalready does browser-level reconnect.Adoption proved it:
createSessionSupervisorDOrunSupervisedTurnrunDurable/runOnWorkflowStepDurableChatTurnEngine(kept, reshaped)SessionSupervisorDOwas dead code in every product.runDurable/runOnWorkflowStepwere unused entirely.runSupervisedTurnhad one caller, stacked on top ofDurableChatTurnEngine(double-buffering the same concern).Diff size
durable/drops from 13 source files to 3 (execution-handle.ts,chat-engine.ts,index.ts)Test plan
pnpm typecheck— cleanpnpm test— 179/179 pass (was 251 — store-matrix tests removed)pnpm lint— cleanpnpm build— successConsumer migration
tax-agent,legal-agent,creative-agent,gtm-agentall use:DurableChatTurnEngine.runTurn(...)→ renamed toChatTurnEngine.runTurn(...), drops thestore/projectId/domain/model/userMessage/workerId/leaseMsinputs; hook signature dropsrecordD1DurableRunStore→ gone (engine no longer needs a store)runSupervisedTurn(gtm only) →box.streamPrompt({ executionId: deriveExecutionId({...}), lastEventId })directlyConsumer PRs follow.