@tangle-network/agent-runtime 0.309.0 → 0.312.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/{activation-BNGcdwKv.js → activation-CE5vCzYS.js} +3 -3
  2. package/dist/{activation-BNGcdwKv.js.map → activation-CE5vCzYS.js.map} +1 -1
  3. package/dist/{activation-CPlj7oAq.d.ts → activation-K-k4BRp9.d.ts} +2 -3
  4. package/dist/agent.d.ts +1 -2
  5. package/dist/agent.js +2 -2
  6. package/dist/{bundle-E6mbRX2P.js → bundle-BK3XBY6q.js} +2 -2
  7. package/dist/{bundle-E6mbRX2P.js.map → bundle-BK3XBY6q.js.map} +1 -1
  8. package/dist/candidate-execution/index.js +5 -5
  9. package/dist/{coordination-driver-nxGtWmJm.js → coordination-driver-Cp2z1zz6.js} +15 -15
  10. package/dist/coordination-driver-Cp2z1zz6.js.map +1 -0
  11. package/dist/{delegate-BH1mMsM-.js → delegate-DPkyIx3C.js} +2 -2
  12. package/dist/{delegate-BH1mMsM-.js.map → delegate-DPkyIx3C.js.map} +1 -1
  13. package/dist/durable.d.ts +1 -2
  14. package/dist/durable.js +4 -4
  15. package/dist/{graph-DEbRy7GI.js → graph-DP0X2bVC.js} +6 -4
  16. package/dist/graph-DP0X2bVC.js.map +1 -0
  17. package/dist/{improvement-cycle-D-GFP8a-.js → improvement-cycle-D5r4auxT.js} +6 -6
  18. package/dist/{improvement-cycle-D-GFP8a-.js.map → improvement-cycle-D5r4auxT.js.map} +1 -1
  19. package/dist/{index-B-VvfcHa.d.ts → index-BdIi1eIV.d.ts} +11281 -2505
  20. package/dist/index.d.ts +4 -6
  21. package/dist/index.js +9 -9
  22. package/dist/intelligence.d.ts +2 -3
  23. package/dist/intelligence.js +7 -7
  24. package/dist/kernel.d.ts +2 -4
  25. package/dist/kernel.js +10 -9
  26. package/dist/{loop-runner-bin-DxH6LwIB.js → loop-runner-bin-Cn7244xv.js} +3 -3
  27. package/dist/{loop-runner-bin-DxH6LwIB.js.map → loop-runner-bin-Cn7244xv.js.map} +1 -1
  28. package/dist/{loop-runner-bin-Bvd0J2OR.d.ts → loop-runner-bin-CoKHGqxE.d.ts} +2 -3
  29. package/dist/loop-runner-bin.d.ts +1 -1
  30. package/dist/loop-runner-bin.js +1 -1
  31. package/dist/mcp/bin.js +3 -3
  32. package/dist/mcp/index.d.ts +2 -5
  33. package/dist/mcp/index.js +5 -5
  34. package/dist/{openai-tools-CP1hPxJd.d.ts → openai-tools-C2GoLNjj.d.ts} +2 -2
  35. package/dist/{prepare-CulUz6Wm.js → prepare-8A1CAWG8.js} +3 -3
  36. package/dist/{prepare-CulUz6Wm.js.map → prepare-8A1CAWG8.js.map} +1 -1
  37. package/dist/{profile-DY9lT3gH.js → profile-BhQShsM1.js} +2 -2
  38. package/dist/{profile-DY9lT3gH.js.map → profile-BhQShsM1.js.map} +1 -1
  39. package/dist/profiles.d.ts +1 -1
  40. package/dist/{protected-model-port-BT2iYAsU.js → protected-model-port-ug52OmvB.js} +4 -4
  41. package/dist/{protected-model-port-BT2iYAsU.js.map → protected-model-port-ug52OmvB.js.map} +1 -1
  42. package/dist/{provision-supervisor-CLGUAePU.js → provision-supervisor-BahNK2L0.js} +4 -4
  43. package/dist/{provision-supervisor-CLGUAePU.js.map → provision-supervisor-BahNK2L0.js.map} +1 -1
  44. package/dist/{runtime-B0td3fc3.js → runtime-DRrpUP7n.js} +9 -9
  45. package/dist/{runtime-B0td3fc3.js.map → runtime-DRrpUP7n.js.map} +1 -1
  46. package/dist/{search-executor-DV-AfHq0.js → search-executor-BuwMU5TF.js} +3 -3
  47. package/dist/{search-executor-DV-AfHq0.js.map → search-executor-BuwMU5TF.js.map} +1 -1
  48. package/dist/{server-B5JWg4Oq.js → server-DMqPF86Y.js} +4 -4
  49. package/dist/{server-B5JWg4Oq.js.map → server-DMqPF86Y.js.map} +1 -1
  50. package/dist/{supervise-B5iHXeuw.js → supervise-CGMzXi_j.js} +64 -8
  51. package/dist/supervise-CGMzXi_j.js.map +1 -0
  52. package/dist/{supervisor-Cd39ZN4b.js → supervisor-DgcmBicU.js} +82 -5
  53. package/dist/supervisor-DgcmBicU.js.map +1 -0
  54. package/dist/testing.d.ts +1 -2
  55. package/dist/testing.js +14 -14
  56. package/dist/tui/index.d.ts +1 -1
  57. package/dist/tui/index.js +1 -1
  58. package/dist/{workspace-streams-DaQceg2p.js → workspace-streams-BEvSX97P.js} +13 -2
  59. package/dist/workspace-streams-BEvSX97P.js.map +1 -0
  60. package/package.json +2 -2
  61. package/skills/profile-authoring/SKILL.md +9 -13
  62. package/dist/coordination-driver-nxGtWmJm.js.map +0 -1
  63. package/dist/graph-DEbRy7GI.js.map +0 -1
  64. package/dist/stream-agent-turn-D-zmKgU2.d.ts +0 -2252
  65. package/dist/substrate-DQnwvsFT.d.ts +0 -284
  66. package/dist/supervise-B5iHXeuw.js.map +0 -1
  67. package/dist/supervisor-Cd39ZN4b.js.map +0 -1
  68. package/dist/types-CFGESVFO.d.ts +0 -6156
  69. package/dist/workspace-streams-DaQceg2p.js.map +0 -1
@@ -1,2252 +0,0 @@
1
- import { $i as ExecCtx, $n as ProviderWorkspaceCaptureReceipt, C as ExecutorToolCall, Ua as AgentTaskStatus, Wa as BackendErrorDetail, X as Scope, Y as Runtime, b as ExecutorRegistry, eo as RuntimeStreamEvent, f as ExecutorCancellation, fn as TraceSource, g as ExecutorFactory, gt as UsageEvent, p as ExecutorCancellationRequest, pa as LoopTokenUsage, rr as ProviderWorkspaceRetentionPort, rt as Spend, s as DefaultVerdict, v as ExecutorNodeContext, wa as Validator, xa as SandboxClient, y as ExecutorProgressEvent, yn as ExecutorProgress, yr as UnavailablePausePolicy } from "./types-CFGESVFO.js";
2
- import { AgentCandidateWorkspaceSnapshotEvidence, AgentProfile, AgentProfileMcpServer, HarnessType, ReasoningEffort, StreamEvent } from "@tangle-network/agent-interface";
3
- import { AgentRunOutcome } from "@tangle-network/sandbox/runtime";
4
- import { ChildProcess } from "node:child_process";
5
- import { WorkspacePlanReceipt } from "@tangle-network/agent-profile-materialize";
6
- import { BackendType, CreateSandboxOptions, PromptOptions, Sandbox, SandboxEvent, SandboxInstance } from "@tangle-network/sandbox";
7
- import { AgentEnvironment as AgentEnvironment$1, AgentEnvironmentCapabilities, AgentEnvironmentCapabilities as AgentEnvironmentCapabilities$1, AgentEnvironmentEvent, AgentEnvironmentEvent as AgentEnvironmentEvent$1, AgentEnvironmentProvider, AgentEnvironmentProvider as AgentEnvironmentProvider$1, AgentEnvironmentQuery, AgentEnvironmentStatus, AgentEnvironmentSummary, AgentProfileRef, AgentSession, AgentSessionRef, AgentSessionStatus as AgentSessionStatus$1, AgentTurnInput, AgentTurnInput as AgentTurnInput$1, AgentTurnResult as AgentTurnResult$2, CheckpointRef, CheckpointRequest, CreateAgentEnvironmentInput, CreateAgentEnvironmentInput as CreateAgentEnvironmentInput$1, ExecRequest, ExecResult, ForkRequest, PlacementInfo, ResourceRequest, WorkspaceRequest } from "@tangle-network/agent-interface/environment-provider";
8
- //#region src/redact.d.ts
9
- /**
10
- * Redaction for values that may leave the Runtime process. The built-in
11
- * redactor is agent-eval's redaction core (`@tangle-network/agent-eval/traces`,
12
- * default profile): credentials found by field name or value shape are
13
- * replaced whole, email, card, SSN and phone values in place, and token counts
14
- * such as `inputTokens` or `max_tokens` are kept. A customer with
15
- * domain-specific PII supplies their own `redact` hook.
16
- *
17
- * This is narrower than `src/sanitize.ts`, which drops fields of the runtime's
18
- * *event envelope* unless the caller opts in: here the value is opaque customer
19
- * payload, so the scrub is value-shaped, not schema-shaped.
20
- *
21
- * @experimental
22
- */
23
- /** A redactor maps an arbitrary trace value to a safe-to-export value. Pure;
24
- * must not throw on cyclic input (the default tolerates cycles). */
25
- type Redactor = (value: unknown) => unknown;
26
- /**
27
- * The built-in redactor. Cycle-safe, depth-bounded and total: it never throws
28
- * on customer input and never mutates it.
29
- */
30
- declare function defaultRedactor(value: unknown): unknown;
31
- /**
32
- * Resolve the redactor a client uses. A caller-supplied hook handles
33
- * domain-specific values first, then the built-in scrubber still removes
34
- * credentials and personal data. Returning `false` is the explicit opt-out for
35
- * already-reviewed public values.
36
- */
37
- declare function resolveRedactor(redact: Redactor | false | undefined): Redactor;
38
- //#endregion
39
- //#region src/mcp/protocol.d.ts
40
- /**
41
- * Shared wire contracts for the in-process stdio MCP servers.
42
- *
43
- * Keeping these types in one module prevents the delegation and generic tool
44
- * servers from accepting subtly different JSON-RPC messages.
45
- *
46
- * @experimental
47
- */
48
- /**
49
- * MCP tool annotations (protocol 2025-03-26 and later). Hints a client reads
50
- * before calling, for example to run a read-only tool without confirmation.
51
- * They describe the tool; they do not enforce anything. @experimental
52
- */
53
- interface McpToolAnnotations {
54
- title?: string;
55
- /** The tool does not modify its environment. */
56
- readOnlyHint?: boolean;
57
- /** The tool may perform destructive updates. Meaningful only when not read-only. */
58
- destructiveHint?: boolean;
59
- /** Repeating a call with the same arguments has no additional effect. */
60
- idempotentHint?: boolean;
61
- /** The tool may reach entities outside its own data, such as the web. */
62
- openWorldHint?: boolean;
63
- }
64
- /** A callable MCP tool exposed by either stdio server. @experimental */
65
- interface McpToolDescriptor {
66
- name: string;
67
- description: string;
68
- inputSchema: Record<string, unknown>;
69
- /** Published in `tools/list` when present. */
70
- annotations?: McpToolAnnotations;
71
- handler: (raw: unknown) => Promise<unknown>;
72
- }
73
- /** Stdio-shaped transport used by the shared JSON-RPC server implementation. @experimental */
74
- interface McpTransport {
75
- input: NodeJS.ReadableStream;
76
- output: NodeJS.WritableStream;
77
- }
78
- /** One JSON-RPC 2.0 request or notification. @experimental */
79
- interface JsonRpcMessage {
80
- jsonrpc: '2.0';
81
- id?: number | string | null;
82
- method: string;
83
- params?: unknown;
84
- }
85
- /** One JSON-RPC 2.0 response. @experimental */
86
- interface JsonRpcResponse {
87
- jsonrpc: '2.0';
88
- id: number | string | null;
89
- result?: unknown;
90
- error?: {
91
- code: number;
92
- message: string;
93
- data?: unknown;
94
- };
95
- }
96
- //#endregion
97
- //#region src/runtime/supervise/peer-mail.d.ts
98
- /**
99
- * What one envelope IS, typed so a reader can act on it without parsing prose.
100
- *
101
- * - `ask` — request a fact the sender lacks; expects an `answer`.
102
- * - `tell` — share a result; MUST carry evidence refs.
103
- * - `challenge` — dispute a peer's claim; MUST cite the refs of the claim it disputes.
104
- * - `answer` — reply to an `ask` or a `challenge`.
105
- */
106
- type PeerMailKind = 'ask' | 'tell' | 'challenge' | 'answer';
107
- /** One admitted peer message. `threadId` is the root mail's id; `depth` is 0 for a root mail and
108
- * one more than its parent for a reply, which is what the reply-depth cap counts. */
109
- interface PeerMailEnvelope {
110
- readonly mailId: string;
111
- readonly threadId: string;
112
- readonly depth: number;
113
- /** The bound sender — resolved from the capability, never from a tool argument. */
114
- readonly from: string;
115
- readonly to: string;
116
- readonly kind: PeerMailKind;
117
- readonly subject: string;
118
- readonly body: string;
119
- /** Evidence the receiver can re-check for itself. Required for `tell` and `challenge`. */
120
- readonly evidenceRefs: ReadonlyArray<string>;
121
- /** The mail id this replies to. Never a coordination question id — a peer cannot address the
122
- * parent's answer channel. */
123
- readonly replyTo?: string;
124
- readonly at: number;
125
- }
126
- /** Why an attempt did not reach a sibling. Each value is a fact the sender can read and act on. */
127
- type PeerMailRefusal = 'sender-unbound' | 'self-addressed' | 'send-quota-exhausted' | 'mailbox-full' | 'thread-depth-exceeded' | 'thread-stopped' | 'unknown-reply-target' | 'evidence-required' | 'subject-too-large' | 'body-too-large' | 'forged-authority' | 'unknown-worker' | 'already-settled' | 'worker-has-no-inbox' | 'scope-stopped' | 'runtime-error';
128
- type PeerMailOutcome = 'delivered' | PeerMailRefusal;
129
- /** The audit record for one attempt — published whether it delivered or was refused, because a
130
- * refused attempt is exactly what a parent auditing a channel needs to see. */
131
- interface PeerMailEvent {
132
- readonly envelope: PeerMailEnvelope;
133
- readonly delivered: boolean;
134
- readonly outcome: PeerMailOutcome;
135
- /** Canonical digest of the exact admitted body, so a later claim can name the bytes it read. */
136
- readonly bodyDigest: string;
137
- readonly error?: string;
138
- }
139
- /** Hard bounds. Every one fails closed with a refusal the sender can read. */
140
- interface PeerMailLimits {
141
- /** Mail one worker may attempt to send for the whole run. */
142
- readonly maxSentPerWorker: number;
143
- /** Mail one worker may receive for the whole run. */
144
- readonly maxInboxPerWorker: number;
145
- /** Total admitted body bytes one worker may receive for the whole run. */
146
- readonly maxInboxBytesPerWorker: number;
147
- /** Maximum reply depth; a root mail is depth 0, so `2` allows ask → answer → answer. */
148
- readonly maxThreadDepth: number;
149
- readonly maxBodyBytes: number;
150
- readonly maxSubjectBytes: number;
151
- }
152
- /** Bounds chosen so a peer channel cannot become the dominant cost of a run: eight sends and
153
- * sixteen receives per worker, 32 KiB of received body, and a reply chain that terminates. */
154
- declare const DEFAULT_PEER_MAIL_LIMITS: PeerMailLimits;
155
- /**
156
- * Phrases that mark the run's AUTHORITY in a folded prompt. A peer that writes one of these is
157
- * trying to speak as the supervisor, so intake refuses the envelope outright.
158
- *
159
- * The render-time fence in the inbox is the second half of this defence and neither half is
160
- * sufficient alone: a fence loses to a body that closes it, and an intake filter loses to a body
161
- * that invents a new authority phrase. Together they make forgery mechanically detectable and give
162
- * the standing prompt one concrete boundary to bind to. Neither makes a model OBEY a boundary.
163
- */
164
- declare const AUTHORITY_MARKERS: ReadonlyArray<string>;
165
- /** The wire property carrying an envelope to a worker inbox. Deliberately its OWN discriminant:
166
- * reusing `steer`/`answer` would let a peer mint a message on the parent's channels. */
167
- declare const PEER_MAIL_WIRE_KEY = "mail";
168
- /** The tool names a mail capability endpoint serves. It serves NOTHING else. */
169
- declare const peerMailVerbNames: readonly ["send_mail", "read_mail"];
170
- /** What a worker sees when it reads its own mailbox. */
171
- interface PeerMailReadout {
172
- /** The reading worker's own id, so a worker can address a reply correctly. */
173
- readonly you: string;
174
- /** Every envelope admitted to this worker so far, oldest first. */
175
- readonly inbox: ReadonlyArray<PeerMailEnvelope>;
176
- /** Live siblings this worker may write to (itself excluded). Without this a worker knows no
177
- * peer's id and the channel is unusable. */
178
- readonly peers: ReadonlyArray<{
179
- readonly workerId: string;
180
- readonly label: string;
181
- }>;
182
- readonly sent: number;
183
- /** Sends still allowed, or `null` when this run set no send quota. */
184
- readonly sendQuotaLeft: number | null;
185
- readonly limits: PeerMailLimits;
186
- }
187
- interface PeerMailSendInput {
188
- readonly to: unknown;
189
- readonly kind: unknown;
190
- readonly subject: unknown;
191
- readonly body: unknown;
192
- readonly evidenceRefs?: unknown;
193
- readonly replyTo?: unknown;
194
- }
195
- interface PeerMailbox {
196
- readonly limits: PeerMailLimits;
197
- /**
198
- * Publish the base URL of the capability listener once it has a port. Until it is set no spawn
199
- * receives a mail endpoint: a capability nobody can reach is not worth handing out, and a URL
200
- * built from an unassigned port would be a lie.
201
- */
202
- setEndpoint(baseUrl: string): void;
203
- /** Mint (idempotently, per assignment) the capability URL for one spawn. Undefined before the
204
- * listener has published its endpoint. */
205
- mintCapability(assignmentId: string): string | undefined;
206
- /** Bind a minted capability to the concrete worker the spawn produced. Until this runs the
207
- * capability can send nothing. */
208
- bindCapability(assignmentId: string, workerId: string): void;
209
- /** Resolve the capability path segment carried in a request URL. */
210
- hasCapability(capabilityId: string): boolean;
211
- /** The two tools a single capability serves, with the sender closed over. */
212
- tools(capabilityId: string): McpToolDescriptor[];
213
- send(capabilityId: string, input: PeerMailSendInput): Promise<PeerMailEvent>;
214
- read(capabilityId: string): PeerMailReadout;
215
- /** The parent's control: refuse every further mail on one thread. Returns false when the thread
216
- * was already stopped. Mail already delivered is not recalled — this stops the next reply. */
217
- stopThread(threadId: string): boolean;
218
- /** Every attempt in order — delivered and refused alike. */
219
- history(): ReadonlyArray<PeerMailEvent>;
220
- }
221
- interface PeerMailboxOptions {
222
- readonly scope: Scope<unknown>;
223
- /** Publish one attempt as a coordination event. Awaited, so a durable subscriber commits the
224
- * record before the sender learns the outcome. */
225
- readonly publish: (event: PeerMailEvent) => Promise<void>;
226
- readonly limits?: Partial<PeerMailLimits>;
227
- readonly now?: () => number;
228
- }
229
- /** True when `text` carries a phrase reserved for the run's authority. Case-insensitive, because
230
- * the render is read by a model and case is not what distinguishes an instruction. */
231
- declare function claimsAuthority(text: string): boolean;
232
- /** True when `value` is an envelope this runtime produced. The worker inbox parses with this, so a
233
- * malformed or partial wire object is discarded rather than rendered as a peer message. */
234
- declare function isPeerMailEnvelope(value: unknown): value is PeerMailEnvelope;
235
- /** Create the run's post office. One per manager scope; the manager's siblings are its addresses. */
236
- declare function createPeerMailbox(opts: PeerMailboxOptions): PeerMailbox;
237
- /**
238
- * The two tools ONE capability serves. `capabilityId` is closed over and `from` is not a parameter,
239
- * so the endpoint a worker holds can only ever speak as that worker. The descriptions carry the
240
- * authority rule, because the receiving model reads them as part of the channel's contract.
241
- */
242
- declare function peerMailTools(mailbox: PeerMailbox, capabilityId: string): McpToolDescriptor[];
243
- //#endregion
244
- //#region src/runtime/router-retry-policy.d.ts
245
- /** Exact retry controls accepted at `AgentProfile.model.metadata.retry`. */
246
- interface RouterRetryPolicy {
247
- /** Total attempts, including the first request. */
248
- readonly maxAttempts?: number;
249
- /** Delay before the second attempt. Later delays grow exponentially. */
250
- readonly initialBackoffMs?: number;
251
- /** Maximum delay between attempts. */
252
- readonly maxBackoffMs?: number;
253
- /** Symmetric random variation around each delay, from 0 through 1. */
254
- readonly jitter?: number;
255
- /** HTTP statuses that may be retried. */
256
- readonly retryStatuses?: ReadonlyArray<number>;
257
- /** Deadline for receiving one attempt's response headers. Zero disables it. */
258
- readonly requestTimeoutMs?: number;
259
- }
260
- //#endregion
261
- //#region src/runtime/tool-loop.d.ts
262
- /** Provider-neutral conversation record accepted by a tool-loop brain. */
263
- type ToolLoopMessageRecord = Record<string, unknown>;
264
- /** One provider-neutral tool request emitted by a tool-loop model. */
265
- interface ToolLoopToolCall {
266
- id: string;
267
- name: string;
268
- /** Raw JSON arguments emitted by the model. */
269
- arguments: string;
270
- }
271
- /** Runtime-owned identity and cancellation for one logical inference call. The wrapper is frozen
272
- * before dispatch; a transport may observe the signal but cannot replace the authority it names. */
273
- interface ToolLoopCallContext {
274
- readonly signal: AbortSignal;
275
- readonly callId: string;
276
- readonly correlationId: string;
277
- }
278
- /** One inference turn over the running conversation + the tool specs → the model's text, any
279
- * tool calls, and token usage. The seam every brain satisfies. */
280
- type ToolLoopChat = (messages: ReadonlyArray<ToolLoopMessageRecord>, tools: ReadonlyArray<ToolSpec>, context?: ToolLoopCallContext) => Promise<{
281
- content?: string | null;
282
- toolCalls: ToolLoopToolCall[];
283
- usage?: {
284
- input: number;
285
- output: number;
286
- reasoning?: number;
287
- };
288
- /** Caller-measured resource totals for this turn; omission makes enforced dimensions unknown. */
289
- resources?: Spend['resources'];
290
- /** Dollar value reported for the turn. It is not billed spend unless provenance says so. */
291
- costUsd?: number;
292
- costProvenance?: 'provider-receipt' | 'billing-receipt' | 'catalog-estimate';
293
- /** The turn ran but its usage was not reported when the transport EXPECTED one (the streamed
294
- * router transport asks for usage and this says it never arrived). A metering caller records an
295
- * unknown turn on it; `runBrainLoop` itself ignores it. */
296
- usageUnknown?: true;
297
- /** Provider-observed model identity. Profile-bound callers validate it before accepting output. */
298
- model?: string;
299
- /** Provider-reported prompt-cache evidence; missing fields remain missing. */
300
- promptCache?: Readonly<Record<string, number | string>>;
301
- /** Physical HTTP/injected-transport attempts spent by this one logical call. */
302
- transportAttempts?: number;
303
- }>;
304
- /** Self-compaction — bound the loop's OWN context window the way a fresh-respawn (dumb-Ralph) loop
305
- * does, but in place. A stateless chat API re-sends the WHOLE running conversation every turn, so an
306
- * agent that accumulates dozens of turns of tool results re-bills its entire transcript on every
307
- * inference — the context-overflow-one-level-up that the conserved budget pool cannot fix. With
308
- * compaction set, once the conversation exceeds `thresholdTokens` the accumulated middle (every prior
309
- * assistant turn + tool result) is distilled into ONE compact progress note and the conversation is
310
- * reset to `[...head, digest]`: the preserved head (system + the original task) survives, the stale
311
- * turn-by-turn history does not. The model keeps deciding; it stops re-billing the whole transcript.
312
- * Fires at a CLEAN turn boundary (after a turn's tool results are folded in, before the next
313
- * inference) so it never orphans an assistant `tool_calls` from its `tool` replies. */
314
- interface ToolLoopCompaction {
315
- /** Compact once the estimated token count of the conversation exceeds this. */
316
- readonly thresholdTokens: number;
317
- /** Distill the conversation into a compact progress note that REPLACES the middle. Receives the
318
- * full conversation (so it can summarize everything done so far); returns the digest string. */
319
- readonly distill: (messages: ReadonlyArray<ToolLoopMessageRecord>) => Promise<string> | string;
320
- /** Leading messages preserved verbatim (system + the original task). Default 2. */
321
- readonly preserveHead?: number;
322
- /** Token estimator over the conversation. Default ≈ chars/4 (incl. tool-call arguments). */
323
- readonly estimateTokens?: (messages: ReadonlyArray<ToolLoopMessageRecord>) => number;
324
- /** Notified each time a compaction fires — for observability/metering. */
325
- readonly onCompact?: (info: {
326
- turn: number;
327
- beforeTokens: number;
328
- afterTokens: number;
329
- }) => void;
330
- }
331
- /** Public supervisor-facing compaction config: same knobs as the primitive, but `distill` is optional
332
- * because the supervisor has a default digest that combines a brain note with live worker state. */
333
- type ToolLoopCompactionOptions = Omit<ToolLoopCompaction, 'distill'> & {
334
- readonly distill?: ToolLoopCompaction['distill'];
335
- };
336
- //#endregion
337
- //#region src/runtime/router-client.d.ts
338
- /**
339
- * Connection details for Runtime's Router-backed executors.
340
- *
341
- * This is deliberately transport-only: model, prompt, tools, generation settings, and retry
342
- * policy belong to the exact executable `AgentProfile` consumed by `streamAgentTurn`.
343
- */
344
- interface RouterTransportConfig {
345
- routerBaseUrl: string;
346
- routerKey: string;
347
- /** Injectable OpenAI-compatible transport. Optional usage.resources carries measured turn totals. */
348
- complete?: (body: Record<string, unknown>, request?: {
349
- readonly headers: Readonly<Record<string, string>>;
350
- readonly signal?: AbortSignal;
351
- }) => Promise<unknown>;
352
- }
353
- /**
354
- * Private request configuration used by Runtime's Router adapter.
355
- *
356
- * Do not export this through a package entry point. Public callers execute a concrete
357
- * `AgentProfile` through `createExecutor` + `streamAgentTurn`; only Runtime may lower that profile
358
- * into these provider request fields.
359
- */
360
- interface RouterConfig extends RouterTransportConfig {
361
- model: string;
362
- /** Exact retry controls lowered from `AgentProfile.model.metadata.retry`. */
363
- retry?: RouterRetryPolicy;
364
- /**
365
- * Optional ceiling for one completion, forwarded as `max_tokens`.
366
- *
367
- * A REASONING model spends this budget on hidden thinking BEFORE it emits a visible token, so
368
- * the default can truncate one mid-thought and return no content at all — observed live with a
369
- * model that spent 8,188 of the 8,192 on reasoning and answered with nothing. Raise it for a
370
- * thinking model; the ceiling belongs to the router and model a caller chose, which is why it
371
- * lives here rather than on one call site.
372
- */
373
- maxTokens?: number;
374
- /**
375
- * Optional ceiling on TOTAL completion tokens — visible answer plus hidden reasoning —
376
- * forwarded as `max_completion_tokens`. Distinct from `maxTokens`: a reasoning model can spend
377
- * an entire `max_tokens` budget on hidden thinking, so only this field bounds what the provider
378
- * bills for one completion.
379
- */
380
- maxCompletionTokens?: number;
381
- /**
382
- * Take the tool-calling completion over SSE instead of one buffered POST. Off by default —
383
- * `routerChatWithTools` never streams, and every existing caller keeps the buffered transport
384
- * byte for byte.
385
- *
386
- * Why it exists: a buffered POST holds one connection idle for the WHOLE completion, and a
387
- * supervisor turn is the longest completion in the system. An intermediary gateway with an
388
- * idle-read timeout kills that connection mid-completion (the 524/503 family). A streamed
389
- * response puts bytes on the wire from the first generated token on, so the connection is only
390
- * idle through prefill. It does NOT shorten prefill, so a gateway whose deadline is
391
- * time-to-FIRST-byte is unaffected; only an idle-timeout gateway is.
392
- *
393
- * Mutually exclusive with `complete`: the injected transport returns one parsed JSON body and has
394
- * no stream to read, so setting both throws rather than silently taking the buffered path.
395
- *
396
- * WHICH PATHS CAN OPT IN. This flag is read in exactly one place (the private `chatWithTools` transport switch), so
397
- * every entry point that takes a caller-supplied `RouterConfig` honors it: `routerBrain`,
398
- * `routerToolLoop`, and `supervisorAgent` (which spreads `deps.router` into the brain's config —
399
- * the supervisor turn this exists for). Two production call sites build a `RouterConfig` literal
400
- * from their own options and therefore CANNOT express it today: the bench strategy's
401
- * `routerToolLoop` config in `strategy.ts` and the local sandbox client's `routerBrain` config in
402
- * `local-sandbox-client.ts`. Neither drives a supervisor-length turn; setting `stream` on a
403
- * config handed to either has no path to reach them, and they stay buffered.
404
- */
405
- stream?: boolean;
406
- }
407
- /** Exact buffered HTTP evidence. Authentication and cookie headers are excluded. */
408
- interface RouterResponseReceipt {
409
- readonly endpoint: string;
410
- readonly callId: string;
411
- readonly attempt: number;
412
- readonly startedAt: string;
413
- readonly endedAt: string;
414
- readonly status: number | null;
415
- readonly error?: {
416
- readonly name: string;
417
- readonly message: string;
418
- };
419
- readonly headers: Readonly<Record<string, string>>;
420
- readonly body: string | null;
421
- }
422
- interface ToolSpec {
423
- type: 'function';
424
- function: {
425
- name: string;
426
- description?: string;
427
- parameters: unknown;
428
- };
429
- }
430
- //#endregion
431
- //#region src/mcp/local-harness.d.ts
432
- /**
433
- * Local coding harness available inside the sandbox — a narrowing of the shared `HarnessType`
434
- * vocabulary, NOT a private spelling of it. The harness id is `claude-code`; `claude` is the
435
- * EXECUTABLE name and lives only in the `command` field below. Keeping one vocabulary is what
436
- * lets a `LocalHarness` be handed straight to the profile materializer and the capability table
437
- * with no translation step.
438
- */
439
- type LocalHarness = Extract<HarnessType, 'claude-code' | 'codex' | 'opencode' | 'pi'>;
440
- /** Every local harness, in table order — the one list `AGENT_RUNTIME_LOCAL_HARNESSES` and any
441
- * other harness enumeration reads, so adding a row above is the only edit a new harness needs. */
442
- declare const LOCAL_HARNESSES: ReadonlyArray<LocalHarness>;
443
- /** The harness a caller gets when it expresses no preference. A composition-root default, not a
444
- * capability claim: one constant so the several entry points cannot drift apart. */
445
- declare const DEFAULT_LOCAL_HARNESS: LocalHarness;
446
- /** The CLI binary a harness id runs. The two are NOT the same string (`claude-code` runs `claude`),
447
- * so anything spawning a harness — a version probe, a login check — reads it from here rather than
448
- * passing the harness id as a command. */
449
- declare function localHarnessExecutable(harness: LocalHarness): string;
450
- /**
451
- * Whether the harness's native control can express this reasoning effort. Admission checks read
452
- * this so a profile the invocation would later refuse is rejected BEFORE any workspace state is
453
- * created, against the same table that emits the argv.
454
- */
455
- declare function harnessSupportsReasoningEffort(harness: LocalHarness, reasoningEffort: ReasoningEffort): boolean;
456
- /** @experimental */
457
- interface RunLocalHarnessOptions {
458
- harness: LocalHarness;
459
- /** Working directory for the subprocess (typically a worktree path). */
460
- cwd: string;
461
- /** Prompt forwarded as the harness CLI's task argument. */
462
- taskPrompt: string;
463
- /**
464
- * Pre-built command + args (e.g. from `harnessInvocation` so the full authored
465
- * `AgentProfile` — systemPrompt + model — reaches the harness). When set it OVERRIDES the
466
- * default prompt-only `buildArgs(taskPrompt)` path; `command` defaults to the harness's
467
- * default binary when only `args` is supplied. When absent the legacy prompt-only shape
468
- * is used unchanged.
469
- */
470
- invocation?: {
471
- command?: string;
472
- args: ReadonlyArray<string>;
473
- };
474
- /** Allow autonomous edits without an interactive approval gate, using whichever bypass argv the
475
- * harness declares. Use only when `cwd` is an isolated candidate worktree. */
476
- dangerouslySkipPermissions?: boolean;
477
- /** Isolate Codex from ambient configuration/instructions and require JSONL token usage.
478
- * The invocation should come from `harnessInvocation(..., { codexReproducible: true })`. */
479
- codexReproducible?: boolean;
480
- /** Absolute host paths that reproducible Codex must not read. The normalized set is compiled
481
- * into the controlled permission profile and its digest is returned in execution evidence. */
482
- codexReadDeniedPaths?: ReadonlyArray<string>;
483
- /** Optional wall-clock kill deadline (ms). Omit it for no timer. A positive value sends
484
- * SIGTERM on expiry. */
485
- timeoutMs?: number;
486
- /** Newest stdout/stderr bytes retained per stream. Default 64 MiB. */
487
- maxOutputBytes?: number;
488
- /** Caller cancellation. SIGTERM is sent on abort. */
489
- signal?: AbortSignal;
490
- /** Override env (defaults to inheriting from the parent). */
491
- env?: NodeJS.ProcessEnv;
492
- /**
493
- * Test seam — inject a custom spawner so unit tests can mock the
494
- * subprocess without touching the OS. Defaults to node's `child_process.spawn`.
495
- */
496
- spawn?: (command: string, args: ReadonlyArray<string>, opts: {
497
- cwd: string;
498
- env: NodeJS.ProcessEnv;
499
- stdio: 'pipe';
500
- detached: boolean;
501
- }) => ChildProcess;
502
- /** Test seam for locating the native Codex executable before it is staged in the worktree. */
503
- resolveCodexExecutable?: (command: string, env: NodeJS.ProcessEnv) => Promise<string>;
504
- }
505
- /**
506
- * Exact aggregate usage emitted by Codex's terminal `turn.completed` JSONL event.
507
- *
508
- * `cachedInputTokens` is a part of `inputTokens` and `reasoningOutputTokens` is a part of
509
- * `outputTokens`; neither adds to the total it describes. `cacheWriteInputTokens` is optional
510
- * because the codex CLI reports it and a provider-normalized capture omits it, and an absent
511
- * counter must stay absent rather than become a zero that claims no cache write was measured.
512
- */
513
- interface CodexTokenUsage {
514
- inputTokens: number;
515
- cachedInputTokens: number;
516
- outputTokens: number;
517
- reasoningOutputTokens: number;
518
- cacheWriteInputTokens?: number;
519
- }
520
- /** Isolation settings asserted before a reproducible Codex run is allowed to start. */
521
- interface CodexExecutionPolicy {
522
- sessionPersistence: 'ephemeral';
523
- userConfig: false;
524
- rules: false;
525
- projectInstructions: false;
526
- skillInstructions: false;
527
- appInstructions: false;
528
- toolSuggestions: false;
529
- multiAgentInstructions: false;
530
- sandbox: 'workspace-write';
531
- permissionProfile: 'agent_runtime_reproducible';
532
- approvalPolicy: 'never';
533
- shellNetwork: false;
534
- webSearch: false;
535
- serviceTier: 'default';
536
- shellEnvironment: 'core-filtered';
537
- loginShell: false;
538
- credentialsReadable: false;
539
- hostHomeReadable: false;
540
- procEnvironment: 'private-sanitized';
541
- sensitiveEnvironmentNamesVisible: false;
542
- parentRepoRead: false;
543
- gitMetadata: false;
544
- temporaryDirectory: 'workspace-private';
545
- stagedExecutable: 'static-elf-read-only';
546
- callerReadDeniedPaths: 'enforced';
547
- containerSockets: false;
548
- }
549
- /** Zero-model-call evidence for the exact Codex process about to run. */
550
- interface CodexExecutionEvidence {
551
- cliVersion: string;
552
- executableSha256: string;
553
- /** SHA-256 of the exact composed prompt argument proved present in the rendered prompt. */
554
- requestedPromptSha256: string;
555
- effectivePromptSha256: string;
556
- nonPromptArgsSha256: string;
557
- controlledConfigSha256: string;
558
- /** Sorted normalized paths compiled into the permission profile. */
559
- readDeniedPaths: string[];
560
- readDeniedPathsSha256: string;
561
- readDeniedPathCount: number;
562
- policy: CodexExecutionPolicy;
563
- }
564
- /** @experimental */
565
- interface LocalHarnessResult {
566
- /** OS exit code. `null` when killed before exit. */
567
- exitCode: number | null;
568
- /** Concatenated stdout. */
569
- stdout: string;
570
- /** Concatenated stderr. */
571
- stderr: string;
572
- /** Set when the process exited via signal (timeout / abort). */
573
- killedBySignal: NodeJS.Signals | null;
574
- /** Wall-clock duration ms (spawn → exit). */
575
- durationMs: number;
576
- /** Set when timeoutMs elapsed before exit. */
577
- timedOut: boolean;
578
- /**
579
- * Set when the caller's AbortSignal fired before this result settled.
580
- * Optional so injected runners and stored results from older releases remain valid.
581
- */
582
- aborted?: boolean;
583
- /** Present for a reproducible Codex run; parsed from the real terminal JSONL event. */
584
- usage?: CodexTokenUsage;
585
- /** Present for reproducible Codex runs; generated and checked before model execution. */
586
- evidence?: CodexExecutionEvidence;
587
- }
588
- /**
589
- * Spawn a local coding harness CLI as a subprocess + collect its output.
590
- *
591
- * NOT responsible for parsing the harness's output or extracting a diff —
592
- * the in-process executor's `streamPrompt` orchestrates `git diff` against
593
- * the worktree after this resolves. This function is intentionally narrow:
594
- * spawn, wait, capture, return.
595
- *
596
- * Fails loud — throws when:
597
- * - `cwd` doesn't exist (subprocess emits ENOENT; surfaced as Error)
598
- * - the harness binary is not on PATH (ENOENT)
599
- * - the caller signal was already aborted before process launch
600
- *
601
- * Does NOT throw when:
602
- * - the subprocess exits non-zero (`result.exitCode` carries the code)
603
- * - a non-reproducible subprocess is aborted / timed out (`result.aborted` /
604
- * `result.timedOut` carries the reason even when a TERM-aware child exits zero)
605
- *
606
- * Reproducible Codex additionally requires a terminal usage event. If cancellation
607
- * prevents that event, this rejects with `CodexExecutionDiagnosticError` instead of
608
- * returning an incomplete reproducibility receipt.
609
- *
610
- * @experimental
611
- */
612
- declare function runLocalHarness(options: RunLocalHarnessOptions): Promise<LocalHarnessResult>;
613
- /**
614
- * Parse and validate the one terminal usage event emitted by `codex exec --json`.
615
- *
616
- * The JSONL framing is this surface's own; the usage RECORD is read by `parseCodexUsageRecord`,
617
- * the one codex usage reader the sandbox decoder also calls, so both surfaces hold the same field
618
- * policy and the same two cross-field invariants.
619
- */
620
- declare function parseCodexTokenUsage(stdout: string): CodexTokenUsage;
621
- //#endregion
622
- //#region src/mcp/worktree.d.ts
623
- /**
624
- *
625
- * Git worktree helpers for the in-process delegation executor. Each
626
- * delegation runs in its own worktree so multiple parallel harness
627
- * subprocesses (claude / codex / opencode in a 3-way fanout) don't clobber
628
- * each other's edits on the shared workspace.
629
- *
630
- * Worktrees live under `<repoRoot>/.agent-worktrees/<runId>/`. After the
631
- * harness exits + the diff is captured, the worktree is removed.
632
- *
633
- * All operations spawn `git` via `child_process.spawn` synchronously
634
- * (via a `runGit` helper). Stays narrow on purpose: no commits, no rebases.
635
- * Diff capture stages all changes (`git add -A`) into the ephemeral worktree's
636
- * index so created (untracked) files appear in the `--cached` diff.
637
- *
638
- * @experimental
639
- */
640
- /** @experimental */
641
- interface WorktreeHandle {
642
- /** Absolute path to the worktree directory. */
643
- path: string;
644
- /** SHA the worktree was created at. */
645
- baseSha: string;
646
- /** Branch name created for this worktree (typically `delegate/<runId>`). */
647
- branch: string;
648
- }
649
- /** @experimental */
650
- interface CreateWorktreeOptions {
651
- /** Absolute path to the main git checkout. */
652
- repoRoot: string;
653
- /** Unique id for the worktree path + branch. Use the delegation run id. */
654
- runId: string;
655
- /** Parent directory the worktree lives under. Defaults to `.agent-worktrees`. */
656
- variantsDir?: string;
657
- /** Override the base ref (default `HEAD`). */
658
- baseRef?: string;
659
- /** Test seam — inject a custom git runner. */
660
- runGit?: GitRunner;
661
- }
662
- /** @experimental */
663
- interface DiffOptions {
664
- /** Worktree to diff. */
665
- worktree: WorktreeHandle;
666
- /** What to compare against. Default `worktree.baseSha`. */
667
- baseRef?: string;
668
- /**
669
- * Repository-relative input paths to omit from the captured worker patch.
670
- * Paths are passed to Git with literal exclusion magic, so profile-provided
671
- * `*`, `?`, `[` and `:` characters can never expand into broader pathspecs.
672
- */
673
- excludePaths?: ReadonlyArray<string>;
674
- /** Test seam. */
675
- runGit?: GitRunner;
676
- }
677
- /** @experimental */
678
- interface DiffResult {
679
- patch: string;
680
- stats: {
681
- filesChanged: number;
682
- insertions: number;
683
- deletions: number;
684
- };
685
- }
686
- /** @experimental */
687
- interface RemoveWorktreeOptions {
688
- worktree: WorktreeHandle;
689
- repoRoot: string;
690
- /** Force removal even if dirty (default true; the loser of a fanout has uncommitted changes). */
691
- force?: boolean;
692
- /** Test seam. */
693
- runGit?: GitRunner;
694
- }
695
- /** Pluggable git runner (sync) — replaceable in tests. */
696
- type GitRunner = (args: ReadonlyArray<string>, opts: {
697
- cwd: string;
698
- }) => {
699
- stdout: string;
700
- stderr: string;
701
- exitCode: number;
702
- };
703
- /** Checkout a fresh git worktree for a delegation run on a new branch under `variantsDir`. @experimental */
704
- declare function createWorktree(options: CreateWorktreeOptions): Promise<WorktreeHandle>;
705
- /** Stage worker changes and return the diff + shortstat, excluding declared input paths. @experimental */
706
- declare function captureWorktreeDiff(options: DiffOptions): Promise<DiffResult>;
707
- /**
708
- * Remove a git worktree and delete its branch. Already-removed paths are harmless; every other
709
- * Git failure rejects so callers cannot report a worktree as destroyed when cleanup failed.
710
- * @experimental
711
- */
712
- declare function removeWorktree(options: RemoveWorktreeOptions): Promise<void>;
713
- //#endregion
714
- //#region src/mcp/worktree-harness.d.ts
715
- /** Outcome of one verification command run in the worktree (test or typecheck). */
716
- interface WorktreeCommandResult {
717
- /** The shell command line that was run. */
718
- command: string;
719
- /** Did the command exit 0? The PASS signal a deliverable gate / coder output reads. */
720
- passed: boolean;
721
- /** OS exit code, or `null` when killed before exit. */
722
- exitCode: number | null;
723
- /** Combined stdout+stderr (capped) — surfaced in traces for diagnosis. */
724
- output: string;
725
- }
726
- /** Proof of the profile inputs delivered before the worker process started. */
727
- interface WorktreeProfileMaterializationReceipt {
728
- /** Digest of the exact materializer plan: files, modes, environment, flags, and unsupported rows. */
729
- workspacePlanDigest: string;
730
- /** Repository-relative profile input files written into the worker worktree. */
731
- writtenPaths: string[];
732
- /** Must be empty on a successful run because this path fails closed. */
733
- unsupported: WorkspacePlanReceipt['unsupported'];
734
- /** Environment variable names added to the worker process. Values remain out of telemetry. */
735
- environmentNames: string[];
736
- /** Exact additional CLI arguments emitted by the materializer. */
737
- flags: string[];
738
- /** `resources.instructions` bypasses native project files so reproducible Codex cannot drop it. */
739
- resourceInstructions: {
740
- delivery: 'none' | 'invocation-prompt';
741
- sha256: string | null;
742
- byteLength: number;
743
- };
744
- }
745
- /** The canonical result of one worktree-harness run, projected by each port to its own shape. */
746
- interface WorktreeHarnessResult {
747
- /** The branch the worktree was cut on (`delegate/<runId>`). */
748
- branch: string;
749
- /** `git diff` of the worktree against its base — the unified patch the harness produced. */
750
- patch: string;
751
- /** Shortstat-derived change counts. */
752
- stats: {
753
- filesChanged: number;
754
- insertions: number;
755
- deletions: number;
756
- };
757
- /**
758
- * Exact profile materialization applied before the harness launched.
759
- * Absent on transports that cannot return a materializer receipt; never fabricated.
760
- */
761
- profileMaterialization?: WorktreeProfileMaterializationReceipt;
762
- /** The harness subprocess outcome. */
763
- harness: {
764
- name: LocalHarness | 'bridge';
765
- exitCode: number | null;
766
- timedOut: boolean;
767
- killedBySignal: NodeJS.Signals | null;
768
- durationMs: number;
769
- stdout: string;
770
- stderr: string;
771
- /** Exact Codex JSONL usage when reproducible mode is enabled. */
772
- usage?: CodexTokenUsage;
773
- /** Installed CLI version captured immediately before execution. */
774
- cliVersion?: string;
775
- /** SHA-256 of the native Codex executable staged read-only in the candidate worktree. */
776
- executableSha256?: string;
777
- /** SHA-256 of the exact composed prompt argument proved present in Codex's rendered prompt. */
778
- requestedPromptSha256?: string;
779
- /** SHA-256 of `codex debug prompt-input` output for the exact isolated prompt. */
780
- effectivePromptSha256?: string;
781
- /** SHA-256 of the exact executable + argv with prompt content replaced by `<PROMPT>`. */
782
- nonPromptArgsSha256?: string;
783
- /** SHA-256 of the isolated config that fixes permissions and shell environment. */
784
- controlledConfigSha256?: string;
785
- /** SHA-256 of the normalized caller-supplied host read-denial paths. */
786
- readDeniedPathsSha256?: string;
787
- /** Sorted normalized caller-supplied host read-denial paths. */
788
- readDeniedPaths?: string[];
789
- /** Number of normalized caller-supplied host read-denial paths. */
790
- readDeniedPathCount?: number;
791
- /** Explicit isolation claims checked before model execution. */
792
- executionPolicy?: CodexExecutionPolicy;
793
- };
794
- /** Verification signals derived in the live worktree (present only when commands were given). */
795
- checks?: {
796
- tests?: WorktreeCommandResult;
797
- typecheck?: WorktreeCommandResult;
798
- };
799
- }
800
- /** The single shell-command-in-worktree runner seam (replaces the per-executor copies). */
801
- type WorktreeCheckRunner = (opts: {
802
- command: string;
803
- cwd: string;
804
- timeoutMs: number;
805
- signal?: AbortSignal;
806
- }) => Promise<{
807
- exitCode: number | null;
808
- output: string;
809
- }>;
810
- /** The canonical result of one in-place harness run. The edits are the DIRECTORY, not a patch:
811
- * the caller supplied the workspace and reads it directly. */
812
- interface InPlaceHarnessResult {
813
- /** The directory the harness ran in, exactly as supplied. */
814
- workspacePath: string;
815
- /** Exact profile materialization applied before the harness launched, and removed after it. */
816
- profileMaterialization: WorktreeProfileMaterializationReceipt;
817
- /** The harness subprocess outcome. */
818
- harness: WorktreeHarnessResult['harness'];
819
- }
820
- //#endregion
821
- //#region src/runtime/provider-placement.d.ts
822
- /** Caller-declared execution placement. Matching never changes the authored profile.
823
- * @experimental */
824
- interface ProviderPlacement {
825
- id: string;
826
- match: {
827
- harness: NonNullable<AgentProfile['harness']>;
828
- provider?: string;
829
- model?: string;
830
- };
831
- create: Omit<Partial<CreateAgentEnvironmentInput>, 'profile' | 'signal' | 'idempotencyKey' | 'requestedId' | 'runtimeAttachments' | 'backend'> & {
832
- backend: string;
833
- };
834
- promptOptions?: ProviderPromptOptions;
835
- }
836
- //#endregion
837
- //#region src/runtime/tangle-sandbox-exact-process-provider.d.ts
838
- type SandboxControlClient = Pick<Sandbox, 'create' | 'get' | 'list'>;
839
- interface CreateTangleSandboxExactProcessProviderOptions {
840
- name?: string;
841
- }
842
- /**
843
- * Adapt Tangle Sandbox's managed control runtime to Runtime's exact-process provider.
844
- *
845
- * The adapter deliberately exposes no ordinary agent environment: an exact experiment
846
- * must start a fresh Sandbox with no managed agent and launch its declared argv directly.
847
- */
848
- declare function createTangleSandboxExactProcessProvider(client: SandboxControlClient, options?: CreateTangleSandboxExactProcessProviderOptions): AgentEnvironmentProvider;
849
- //#endregion
850
- //#region src/runtime/environment-provider.d.ts
851
- /** Provider object or registry name accepted by runtime provider adapters.
852
- * @experimental */
853
- type AgentEnvironmentProviderRef = AgentEnvironmentProvider | string;
854
- /** In-memory registry for named `AgentEnvironmentProvider` instances.
855
- * @experimental */
856
- interface AgentEnvironmentProviderRegistry {
857
- register(provider: AgentEnvironmentProvider, options?: {
858
- replace?: boolean;
859
- }): void;
860
- has(name: string): boolean;
861
- get(name: string): AgentEnvironmentProvider | undefined;
862
- require(name: string): AgentEnvironmentProvider;
863
- names(): string[];
864
- providers(): AgentEnvironmentProvider[];
865
- capabilities(name: string): Promise<AgentEnvironmentCapabilities>;
866
- }
867
- /** Create a registry that resolves provider names to concrete provider instances.
868
- * @experimental */
869
- declare function createAgentEnvironmentProviderRegistry(providers?: Iterable<AgentEnvironmentProvider>): AgentEnvironmentProviderRegistry;
870
- /** Resolve a provider instance or registry name, failing loudly when a name is unknown.
871
- * @experimental */
872
- declare function resolveAgentEnvironmentProvider(provider: AgentEnvironmentProviderRef, registry?: AgentEnvironmentProviderRegistry): AgentEnvironmentProvider;
873
- /** Options for exposing an `AgentEnvironmentProvider` through the legacy sandbox client port.
874
- * @experimental */
875
- interface ProviderAsSandboxClientOptions {
876
- defaults?: Partial<CreateAgentEnvironmentInput>;
877
- requireTerminalEvent?: boolean;
878
- /** Require declared live continuation plus concrete session controls. */
879
- requireSession?: boolean;
880
- /** Verified workspace capture before every environment delete, including steerable sessions. */
881
- workspaceRetention?: ProviderWorkspaceRetentionPort;
882
- /** Supervised identity assigned before the environment is created. */
883
- retentionIdentity?: {
884
- executionId: string;
885
- profile: AgentProfile;
886
- node?: ExecutorNodeContext;
887
- };
888
- /** Called after verification and before deletion so the run result can retain the exact join. */
889
- onWorkspaceCaptured?: (receipt: ProviderWorkspaceCaptureReceipt) => void | Promise<void>;
890
- mapCreateOptions?: (options: CreateSandboxOptions | undefined) => Partial<CreateAgentEnvironmentInput>;
891
- }
892
- /** Adapt a neutral environment provider to the `SandboxClient` interface used by existing loop paths.
893
- * @experimental */
894
- declare function providerAsSandboxClient(provider: AgentEnvironmentProvider, options?: ProviderAsSandboxClientOptions): SandboxClient;
895
- /**
896
- * What one provider-executed turn settles on: the visible answer plus the event archive the
897
- * environment streamed. It is the value a `ProviderExecutorOptions.validator` scores.
898
- *
899
- * The archive is the streamed sequence, in order, without superseded part updates. A harness
900
- * streams a text or reasoning part cumulatively: every `message.part.updated` frame restates the
901
- * part's whole text so far. When a later frame of the same part extends a frame's text, the
902
- * earlier frame is left out, so each such part is archived once, at its latest frame. Keeping
903
- * every frame retains frames times text length, and the settled archive is hashed and stored.
904
- * A frame that does not extend the part's text is kept, and every other event is kept verbatim.
905
- * Read a part's text from `part.text`; a retained frame's `delta` is only that frame's increment.
906
- *
907
- * @experimental
908
- */
909
- interface ProviderLeafOut {
910
- content: string;
911
- events: AgentEnvironmentEvent[];
912
- /** Portable executable workspace evidence accepted before the source environment was deleted. */
913
- workspaceSnapshot?: AgentCandidateWorkspaceSnapshotEvidence;
914
- /** Exact provider and supervisor identities bound to the retained bytes. */
915
- workspaceCapture?: ProviderWorkspaceCaptureReceipt;
916
- /**
917
- * Why workspace retention produced no receipt for this settled turn. The source environment is
918
- * preserved as evidence and the turn's result stands.
919
- */
920
- workspaceCaptureFailure?: string;
921
- /** How many streamed part updates the archive left out because a later frame superseded them. */
922
- supersededPartUpdates?: number;
923
- }
924
- /**
925
- * Per-run Sandbox prompt options for the provider path — the same field, the same name, and the
926
- * same kernel-owned exclusions as `ExecCtx.promptOptions` on the sandbox path.
927
- *
928
- * The kernel owns `sessionId` and `signal`, so neither is declarable: a caller-chosen session id
929
- * would make every worker share one server session, and the abort channel belongs to the run.
930
- * `model` is excluded too, and for a different reason: this executor's materialization record
931
- * names the model from `AgentProfile`, so a turn-level override would make the record state a
932
- * model the provider did not run. Declare the instrument on `AgentProfile.model`.
933
- *
934
- * Everything else is the per-call configuration a portable profile cannot carry. `backend` is the
935
- * load-bearing one: `backend.model.authMode` plus `authFiles` is how a caller-owned subscription
936
- * seat reaches the harness inside the environment. Runtime lowers these onto the turn with the one
937
- * mapper it already uses in the other direction, so a sandbox-shaped provider reads them from
938
- * `AgentTurnInput.providerOptions.backend` exactly as it reads a sandbox box's prompt options.
939
- *
940
- * @experimental
941
- */
942
- type ProviderPromptOptions = Omit<PromptOptions, 'model' | 'sessionId' | 'signal'>;
943
- /** Options for running a provider as a supervise-mode executor.
944
- * @experimental */
945
- interface ProviderExecutorOptions {
946
- /** Select exactly one caller-declared placement from each child's unchanged profile. */
947
- placements?: readonly ProviderPlacement[];
948
- defaults?: Partial<CreateAgentEnvironmentInput>;
949
- runtime?: Runtime;
950
- destroyOnSettle?: boolean;
951
- requireTerminalEvent?: boolean;
952
- /**
953
- * Per-run prompt options merged UNDER every streamed turn: a mapped turn's own field wins, and
954
- * the runtime's abort signal is applied last. `providerOptions` merges one level, so a
955
- * `taskToTurn` that sets its own provider option cannot silently drop the session credential
956
- * declared here.
957
- */
958
- promptOptions?: ProviderPromptOptions;
959
- /**
960
- * OPT-IN executable score for this worker, with the SAME contract the sandbox seam's validator
961
- * has: `validate` runs while the environment is still alive, so `ValidationCtx.box` can read
962
- * files and run commands in the environment it is scoring. Every other supervised hook fires
963
- * after teardown and can only read the artifact.
964
- * `ValidationCtx.node` identifies the supervised node, including its recursion depth, so a
965
- * shared validator can apply a root-only contract without applying it to nested managers.
966
- *
967
- * The verdict becomes the settled artifact's verdict. Absent, nothing changes and the leaf falls
968
- * back to its own settle verdict.
969
- */
970
- validator?: Validator<ProviderLeafOut>;
971
- /** Capture and verify a portable executable workspace before Runtime destroys the environment. */
972
- workspaceRetention?: ProviderWorkspaceRetentionPort;
973
- /** Transform only the profile sent to `provider.create`. The original profile
974
- * remains the input to `taskToTurn`, so execution-only normalization cannot
975
- * rewrite the caller's task mapping. */
976
- profileForCreate?: (profile: AgentProfile) => AgentProfile;
977
- /** Map the task while retaining the kernel's canonical prompt mapping by default. */
978
- taskToTurn?: (task: unknown, specProfile: AgentProfile, defaultTurn: AgentTurnInput) => AgentTurnInput;
979
- /**
980
- * How a supervised leaf waits out an upstream that cannot serve now.
981
- *
982
- * When the model provider refuses a leaf's turn for capacity (a quota, a rate limit, an
983
- * overload, or the router's own refused credential; see `upstreamUnavailableSignal`), the leaf
984
- * keeps its environment, pauses, and continues in the same environment and session with a
985
- * short instruction to pick up where it stopped. The pause starts at `unavailablePauseMs`
986
- * (15 s) and doubles to `maxUnavailablePauseMs` (5 min), the same rule a driver follows. Only
987
- * the leaf's deadline, cancellation and budget end it. Each pause is journaled as a `paused`
988
- * spawn event, and each continuation as the node's next `execution-input`.
989
- *
990
- * Applies to a retained execution under a Scope, the path a supervised leaf takes on a provider
991
- * that declares `retainedControl`. Workspace retention verifies each invocation separately;
992
- * failed capture or required incomplete coverage preserves the source and stops continuation.
993
- * `false` ends the execution on the refused turn.
994
- */
995
- unavailablePause?: UnavailablePausePolicy | false;
996
- }
997
- /** Adapt an environment provider into an `ExecutorFactory` for `createExecutor`.
998
- *
999
- * `createExecutor({ backend: 'provider', provider })` is the composition most callers want; it
1000
- * builds this factory and injects the seam. See `examples/provider-executor/`.
1001
- *
1002
- * Still `@experimental`: the entry point that consumes it, `createExecutor`, carries no stability
1003
- * tag and is therefore experimental by default, so a stable promise here would be reachable only
1004
- * through an experimental symbol.
1005
- *
1006
- * @experimental */
1007
- declare function providerAsExecutor(provider: AgentEnvironmentProvider, options?: ProviderExecutorOptions): ExecutorFactory<unknown>;
1008
- //#endregion
1009
- //#region src/runtime/sandbox-events.d.ts
1010
- /** The provider/model the platform reports it actually bound to a turn, when it reports one.
1011
- * `source` is the platform's own account of where that choice came from — `environment` means
1012
- * the platform chose, not the request. */
1013
- interface SandboxServedBackend {
1014
- readonly provider?: string;
1015
- readonly model?: string;
1016
- readonly source?: string;
1017
- }
1018
- /**
1019
- * Read the served execution identity off one Sandbox event.
1020
- *
1021
- * The platform reports `effectiveBackend` on `execution.started` and again on the terminal
1022
- * event (`@tangle-network/sandbox`, `EffectiveBackend`). Absence returns `undefined`
1023
- * and must stay unknown — a request is not a receipt, so nothing here may be inferred from
1024
- * what was asked for.
1025
- */
1026
- declare function sandboxEventServedBackend(event: SandboxEvent): SandboxServedBackend | undefined;
1027
- /**
1028
- * Fail the execution when the platform reports serving a model other than the exact one asked for.
1029
- *
1030
- * Measured motive (agent-runtime#892, live infrastructure 2026-08-17): 6 of 6 boxes whose profile
1031
- * declared `zai-coding-plan/glm-5.2` reported
1032
- * `{"provider":"openai-compat","model":"deepseek/deepseek-v4-flash","source":"environment"}`,
1033
- * while the materialization receipt recorded the declared model as `status: "known"`. Sending
1034
- * `backend.model` makes that substitution unlikely; only reading the report back makes it
1035
- * detectable. A run that cannot say which model produced its evidence must not settle as one
1036
- * that can.
1037
- *
1038
- * Silent when the platform reports no served model: unobserved stays unobserved.
1039
- */
1040
- declare function assertSandboxServedModel(event: SandboxEvent, expected: {
1041
- readonly provider?: string;
1042
- readonly model?: string;
1043
- } | undefined): void;
1044
- /**
1045
- * Extract a `RuntimeStreamEvent`-shaped `llm_call` from a sandbox event when
1046
- * the event carries usage/cost data. Returns `undefined` for non-cost events
1047
- * so the kernel can iterate the full stream without branching.
1048
- *
1049
- * Pure by contract: it never throws on a failed run. The terminal truth
1050
- * boundary is the public Sandbox outcome tracker, applied after the complete
1051
- * stream. Post-hoc readers — {@link sumSandboxUsage}, the
1052
- * analyst trace store, the chat projection — must stay able to read a failed
1053
- * turn's events, which is when reading them matters most.
1054
- *
1055
- * Canonical cost-carrying types observed in the wild:
1056
- * - `llm_call` — `data: { model, tokensIn, tokensOut, costUsd, ... }`
1057
- * - `message.completed` / `result` — `data: { usage: { inputTokens,
1058
- * outputTokens, totalCostUsd? } }`
1059
- * - `cost.usage` / `usage` — same shape under a dedicated type
1060
- *
1061
- * Numeric coercion is strict: `Number.isFinite` gates every accumulator write
1062
- * so a sentinel `NaN` from a misbehaving backend cannot poison the ledger.
1063
- */
1064
- declare function extractLlmCallEvent(event: SandboxEvent, agentRunName: string): (RuntimeStreamEvent & {
1065
- type: 'llm_call';
1066
- }) | undefined;
1067
- /**
1068
- * Per-turn usage accounting over BOTH the canonical events and the harness-native ones.
1069
- *
1070
- * Some harnesses report a turn's tokens only inside their own event (`harness-usage.ts`), and a
1071
- * stream may carry that report AND a canonical usage event for the same turn. Crediting both
1072
- * counts one turn twice, so this ledger holds the precedence rule: a canonical usage event WINS,
1073
- * and a harness-native report is credited only for a turn in which no canonical usage arrived.
1074
- *
1075
- * The harness-native report is held until the turn ends, because it can arrive before the
1076
- * canonical answer is known — codex emits `turn.completed` ahead of the terminal transport
1077
- * events. Call {@link SandboxUsageLedger.observe} for every event of a turn, then
1078
- * {@link SandboxUsageLedger.settleTurn} once at the turn boundary; settling also resets the
1079
- * ledger for the next turn, so one ledger serves a whole multi-turn session.
1080
- *
1081
- * The ledger never throws on a receipt it cannot read: `observe` returns a receipt with
1082
- * `tokensKnown: false` and `tokensUnknownReason`, so one policy serves every consumer.
1083
- */
1084
- interface SandboxUsageLedger {
1085
- /** Cumulative worker tokens, including cache classifications reported after the prompt total. */
1086
- tokenUsage(): LoopTokenUsage;
1087
- /** Account one event. Returns the canonical usage receipt to credit now, if the event is one. */
1088
- observe(event: SandboxEvent, agentRunName: string): (RuntimeStreamEvent & {
1089
- type: 'llm_call';
1090
- }) | undefined;
1091
- /** End the turn. Returns the held harness-native receipt when no canonical usage arrived. */
1092
- settleTurn(agentRunName: string): (RuntimeStreamEvent & {
1093
- type: 'llm_call';
1094
- }) | undefined;
1095
- }
1096
- /** A {@link SandboxUsageLedger} for one worker. Pass the worker's harness to decode with that
1097
- * harness's adapter; omit it to try every registered adapter. */
1098
- declare function createSandboxUsageLedger(harness?: HarnessType): SandboxUsageLedger;
1099
- /**
1100
- * Sum the token usage + USD cost of a sandbox turn's events — the one honest way to meter an
1101
- * `openSandboxRun` cell. Folds a {@link SandboxUsageLedger} over the stream, so it reads usage off
1102
- * EVERY backend event shape — the canonical events plus a harness that reports usage only in its
1103
- * own event — and a `runProfileMatrix` dispatch can report it to `ctx.cost`:
1104
- *
1105
- * receipt: (turn) => {
1106
- * const u = sumSandboxUsage(turn.events)
1107
- * return { model, inputTokens: u.input, outputTokens: u.output,
1108
- * ...(u.tokensKnown === false ? { usageUnknown: true } : {}),
1109
- * ...(u.usdKnown !== false && u.costUsd > 0 ? { actualCostUsd: u.costUsd } : {}),
1110
- * ...(u.usdKnown === false ? { costUnknown: true } : {}),
1111
- * ...(u.estimatedCostUsd !== undefined ? { estimatedCostUsd: u.estimatedCostUsd } : {}) }
1112
- * }
1113
- *
1114
- * Without this a cell reads `{tokens:0, cost:0}` and the backend-integrity guard correctly aborts the
1115
- * matrix as a stub. `agentRunName` is the fallback model label for cost-only events (default `'agent'`).
1116
- *
1117
- * Pure by contract, like the ledger it folds: it never throws. A harness receipt the ledger cannot
1118
- * read leaves the result at `tokensKnown: false` with `tokensUnknownReason` carrying the decode
1119
- * message — an unreadable receipt is a different fact from a turn that reported no usage, and a
1120
- * post-hoc reader that threw would lose the whole failed turn it exists to report.
1121
- */
1122
- declare function sumSandboxUsage(events: readonly SandboxEvent[], agentRunName?: string): {
1123
- input: number;
1124
- output: number;
1125
- costUsd: number;
1126
- tokensKnown?: false;
1127
- usdKnown?: false;
1128
- estimatedCostUsd?: number;
1129
- tokensUnknownReason?: string;
1130
- };
1131
- /**
1132
- * Cross-event state for {@link mapSandboxToolEvent}. Sandbox backends emit a
1133
- * tool invocation as MANY `message.part.updated` frames on the same call id
1134
- * (pending → running → completed), so faithful projection needs per-call
1135
- * status memory: one `tool_call` on first sighting, at most one `tool_result`
1136
- * on the terminal transition, nothing on intermediate re-frames. Create one
1137
- * state per turn via {@link createSandboxToolPartState}.
1138
- *
1139
- * @experimental
1140
- */
1141
- interface SandboxToolPartState {
1142
- /** Last seen status per tool call id. A terminal status is sticky — later
1143
- * frames on a settled call project to nothing. */
1144
- statusByCall: Map<string, string>;
1145
- /** Sequence for synthesized call ids when an event carries none. */
1146
- seq: number;
1147
- }
1148
- /**
1149
- * Fresh per-turn {@link SandboxToolPartState} for {@link mapSandboxToolEvent} — an
1150
- * empty call-status map so each turn projects tool frames independently.
1151
- *
1152
- * @experimental
1153
- */
1154
- declare function createSandboxToolPartState(): SandboxToolPartState;
1155
- /**
1156
- * Project one `SandboxEvent` onto the `tool_call` / `tool_result` variants of
1157
- * `RuntimeStreamEvent` — the tool-part projection `mapSandboxEvent`
1158
- * deliberately does NOT perform. Opt-in and additive: `mapSandboxEvent`'s
1159
- * default vocabulary (text/reasoning deltas + `llm_call`) is unchanged;
1160
- * consumers that need the tool surface (chat UIs rendering tool activity)
1161
- * compose this projector alongside it — `streamAgentTurn` does exactly that
1162
- * under its `preserveToolParts` option.
1163
- *
1164
- * Handled shapes (observed on the opencode / claude-code sandbox backends):
1165
- * - `message.part.updated` with `part.type === 'tool'` — stateful: a
1166
- * `tool_call` on the call id's first frame (args from `state.input` or
1167
- * `state.metadata.input`), a `tool_result` when the status transitions to
1168
- * `completed` (result from `state.output` / `metadata.output`) or to a
1169
- * terminal failure (result is `{ error, status, output? }` — the error
1170
- * surfaced in-band, never dropped).
1171
- * - bare `tool*` event types (`tool.call`, `tool_result`, …) — stateless:
1172
- * `*result*` types project to `tool_result`, the rest to `tool_call`.
1173
- *
1174
- * Returns `[]` for every non-tool event.
1175
- *
1176
- * @experimental
1177
- */
1178
- declare function mapSandboxToolEvent(event: SandboxEvent, state: SandboxToolPartState): (RuntimeStreamEvent & {
1179
- type: 'tool_call' | 'tool_result';
1180
- })[];
1181
- /**
1182
- * Project one `SandboxEvent` onto the `RuntimeStreamEvent` chat-UX vocabulary,
1183
- * for runtimes that bridge a sandbox `streamPrompt` into the
1184
- * `AgentRuntime.act` streaming contract. Returns `undefined` for events that
1185
- * have no faithful projection — the raw stream is preserved separately for the
1186
- * `OutputAdapter`, so an unmapped event never loses data.
1187
- *
1188
- * Mapped (the task-optional incremental variants — no synthesized task
1189
- * lifecycle, no guessed tool-part shapes):
1190
- * - `message.part.updated` text part → `text_delta`
1191
- * - `message.part.updated` reasoning/thinking part → `reasoning_delta`
1192
- * - cost-bearing events → `llm_call` (shared with the ledger extractor)
1193
- *
1194
- * Tool parts are deliberately NOT mapped here (unchanged default) — compose
1195
- * {@link mapSandboxToolEvent} alongside when a consumer needs them.
1196
- *
1197
- * The opencode backend emits incremental text as
1198
- * `{ type: 'message.part.updated', data: { part: { type, text }, delta } }`;
1199
- * `delta` is the increment, `part.text` the running accumulation.
1200
- */
1201
- declare function mapSandboxEvent(event: SandboxEvent, opts?: {
1202
- agentRunName?: string;
1203
- }): RuntimeStreamEvent | undefined;
1204
- /**
1205
- * Project one `SandboxEvent` onto Runtime's executor progress vocabulary: incremental text and
1206
- * reasoning, tool calls and results, and an interaction request. It composes the existing
1207
- * projections ({@link mapSandboxEvent}, {@link mapSandboxToolEvent}, and the canonical Agent
1208
- * Interface decode) so every sandbox-shaped executor publishes live output through one reader.
1209
- * Usage-bearing events project to nothing here — accounting stays on the `tokens`/`cost`
1210
- * channels.
1211
- *
1212
- * Pass one {@link SandboxToolPartState} per turn so a multi-frame tool call yields one call and
1213
- * at most one result.
1214
- *
1215
- * @experimental
1216
- */
1217
- declare function sandboxProgressEvents(event: SandboxEvent, state: SandboxToolPartState): ExecutorProgressEvent[];
1218
- //#endregion
1219
- //#region src/runtime/sandbox-executor-output.d.ts
1220
- /**
1221
- * What a settled turn produced, as an explicit marker.
1222
- *
1223
- * `text` carries the byte length of the answer, `empty` says a text-bearing terminal event was
1224
- * observed and carried nothing, and `absent` says no text-bearing event was observed at all.
1225
- * The three are distinct on purpose: an empty settle blob used to be indistinguishable from lost
1226
- * output, so a reader could not tell a box that produced nothing from one whose answer never
1227
- * arrived.
1228
- */
1229
- type SandboxOutputMarker = {
1230
- readonly kind: 'text';
1231
- readonly bytes: number;
1232
- } | {
1233
- readonly kind: 'empty';
1234
- } | {
1235
- readonly kind: 'absent';
1236
- };
1237
- /** Parsed output of one Sandbox executor turn. */
1238
- interface SandboxLeafOut {
1239
- events: SandboxEvent[];
1240
- /** The observed answer. `undefined` when no text-bearing event was observed — never `''`. */
1241
- content: string | undefined;
1242
- /** Explicit account of what the turn produced. */
1243
- output: SandboxOutputMarker;
1244
- /**
1245
- * Provider and model the platform reported serving this turn, when it reported one. Absent means
1246
- * the platform said nothing; it is never filled from the request, because a request is not a
1247
- * receipt.
1248
- */
1249
- servedBackend?: SandboxServedBackend;
1250
- toolCalls?: ExecutorToolCall[];
1251
- outcome?: AgentRunOutcome;
1252
- }
1253
- //#endregion
1254
- //#region src/runtime/shared-box.d.ts
1255
- /**
1256
- * The most workers one default box carries at once.
1257
- *
1258
- * It keeps a worker count that the 512-task pids limit of a Tangle box can hold with headroom
1259
- * for the sidecar's own processes and the workers' tool shells: 118 idle tasks plus 8 workers at
1260
- * about 34 tasks each is 390. Memory is not the binding limit: 8 workers used about 4 GB in a
1261
- * 16 GB box. Raise it only for a box whose pids limit was raised with it.
1262
- */
1263
- declare const DEFAULT_SHARED_BOX_WORKERS = 8;
1264
- /** The box shape the default placement creates: memory for 8 workers at about 0.5 GB each plus
1265
- * the sidecar, and 4 cores, because the workers wait on the model most of the time. */
1266
- declare const DEFAULT_SHARED_BOX_RESOURCES: Readonly<{
1267
- cpuCores: 4;
1268
- memoryMB: 8192;
1269
- }>;
1270
- /** The request header the router records as a usage row's `clientName`. */
1271
- declare const ROUTER_CLIENT_HEADER = "x-tangle-client";
1272
- /**
1273
- * The router client name of the worker that runs supervised node `nodeId`.
1274
- *
1275
- * The node id is already in the spawn journal, so a keeper joins a router row to a node without
1276
- * a new record: the row whose `clientName` is `agent-runtime-node/<nodeId>` is that node's spend.
1277
- */
1278
- declare function sharedWorkerClientName(nodeId: string): string;
1279
- /** The Sandbox box surface a shared box uses. The Sandbox SDK's `SandboxInstance` satisfies it. */
1280
- interface SharedBoxHandle {
1281
- readonly id: string;
1282
- exec(command: string, options?: {
1283
- timeoutMs?: number;
1284
- }): Promise<{
1285
- exitCode?: number | null;
1286
- stdout?: string;
1287
- stderr?: string;
1288
- }>;
1289
- readonly fs: {
1290
- read(path: string): Promise<string>;
1291
- write(path: string, content: string): Promise<unknown>;
1292
- };
1293
- readonly process: {
1294
- spawnExact(executable: string, args: readonly string[], options?: {
1295
- cwd?: string;
1296
- env?: Record<string, string>;
1297
- timeoutMs?: number;
1298
- }): Promise<SharedBoxProcess>;
1299
- list(): Promise<ReadonlyArray<{
1300
- pid: number;
1301
- command?: string;
1302
- cwd?: string;
1303
- }>>;
1304
- get(pid: number): Promise<SharedBoxProcess | null>;
1305
- };
1306
- delete(): Promise<unknown>;
1307
- }
1308
- /** One process in a shared box, as the Sandbox SDK's process manager returns it. */
1309
- interface SharedBoxProcess {
1310
- readonly pid: number;
1311
- wait(): Promise<number>;
1312
- kill(signal?: 'SIGTERM' | 'SIGKILL', options?: {
1313
- tree?: boolean;
1314
- }): Promise<void>;
1315
- stdout(): AsyncIterable<string>;
1316
- stderr(): AsyncIterable<string>;
1317
- }
1318
- /** Options for {@link sharedBoxPlacement}. */
1319
- interface SharedBoxPlacementOptions {
1320
- /** The Sandbox client that creates and deletes the shared boxes. */
1321
- client: SandboxClient;
1322
- /**
1323
- * Create options for every shared box: resources, egress, secrets, lifetime and billing.
1324
- * Runtime owns `backend`, so the box's sidecar never serves a worker turn.
1325
- */
1326
- box?: Omit<CreateSandboxOptions, 'backend'>;
1327
- /** The most workers one box carries at once. Defaults to {@link DEFAULT_SHARED_BOX_WORKERS}. */
1328
- workersPerBox?: number;
1329
- /** Absolute directory in each box that holds one directory per worker. */
1330
- workerRoot?: string;
1331
- /** First wait before repeating a Sandbox call that failed transiently; doubles per attempt. */
1332
- retryDelayMs?: number;
1333
- /**
1334
- * How a worker waits out a model provider that cannot serve now: a quota, a rate limit, an
1335
- * overload, or the router's own refused credential (`unavailableSignalOfFailure`). The worker
1336
- * pauses 15 s, doubling to 5 min, the rule a driver and a dedicated leaf follow, then continues
1337
- * its own opencode session in its own directory. Only cancellation, the turn's `timeoutMs` and
1338
- * the node's deadline and budget, which abort the turn, end the pauses. `false` ends the turn on
1339
- * the refused run.
1340
- */
1341
- unavailablePause?: UnavailablePausePolicy | false;
1342
- }
1343
- /** Counts that show how the pool placed its workers. */
1344
- interface SharedBoxStats {
1345
- readonly boxesCreated: number;
1346
- readonly boxesLive: number;
1347
- readonly workersPlaced: number;
1348
- readonly workersLive: number;
1349
- /** The most workers any one box carried at the same time. */
1350
- readonly peakWorkersPerBox: number;
1351
- /** Every box this pool created, with the workers it served. */
1352
- readonly boxes: ReadonlyArray<{
1353
- readonly id: string;
1354
- readonly workersServed: number;
1355
- readonly deleted: boolean;
1356
- /** Milliseconds from the create request to a box that runs processes. */
1357
- readonly createMs: number;
1358
- }>;
1359
- }
1360
- /** What {@link SharedBoxPlacement.close} did to each box. */
1361
- interface SharedBoxCloseReceipt {
1362
- readonly boxId: string;
1363
- readonly deleted: boolean;
1364
- readonly error?: string;
1365
- }
1366
- /** Who a shared worker is, for the router rows its model calls leave on the box's key. */
1367
- interface SharedWorkerIdentity {
1368
- /** The supervised node the worker runs. Absent for a worker outside a supervised tree. */
1369
- readonly nodeId?: string;
1370
- }
1371
- /** A shared-box placement: the provider Runtime uses for a profile the placement accepts. */
1372
- interface SharedBoxPlacement {
1373
- /**
1374
- * The environment provider that places one accepted worker in a shared box. A worker with a
1375
- * `nodeId` sends `x-tangle-client: agent-runtime-node/<nodeId>` on its router calls; without
1376
- * one, its calls carry no client name and stay charged to the box.
1377
- */
1378
- providerFor(worker?: SharedWorkerIdentity): AgentEnvironmentProvider;
1379
- /** Public identity of this placement, recorded on every execution it serves. */
1380
- readonly identity: {
1381
- readonly id: string;
1382
- readonly digest: string;
1383
- };
1384
- /** Why a shared box cannot carry this profile, or `undefined` when it can. */
1385
- refusal(profile: AgentProfile): string | undefined;
1386
- stats(): SharedBoxStats;
1387
- /** Delete every box this placement created and still holds. Call it when the run settles. */
1388
- close(): Promise<ReadonlyArray<SharedBoxCloseReceipt>>;
1389
- }
1390
- /**
1391
- * Why a shared box cannot carry `profile`, or `undefined` when it can.
1392
- *
1393
- * A shared box carries a profile whose whole behavior reaches an opencode process through its own
1394
- * working directory, its own configuration and the box's own model credential. Everything else
1395
- * keeps a dedicated box: another harness, a model the box credential cannot reach, a replaced
1396
- * system prompt, MCP servers, connections, hooks, subagents, extensions, tool grants, and any
1397
- * resource the materializer does not lower into the worker's directory.
1398
- */
1399
- declare function sharedBoxRefusal(profile: AgentProfile): string | undefined;
1400
- /**
1401
- * Place accepted workers as processes in a pool of shared Sandbox boxes.
1402
- *
1403
- * The pool fills a box before it creates the next one and deletes a box when its last worker
1404
- * releases it. Runtime reaches this placement through `createExecutor({ backend: 'provider',
1405
- * shared })`: a profile that {@link SharedBoxPlacement.refusal} accepts runs here, and every other
1406
- * profile keeps the dedicated provider.
1407
- */
1408
- declare function sharedBoxPlacement(options: SharedBoxPlacementOptions): SharedBoxPlacement;
1409
- //#endregion
1410
- //#region src/runtime/harness-usage.d.ts
1411
- /**
1412
- * One harness's own token-usage report for one turn, in the runtime's field names.
1413
- *
1414
- * `input` is the provider's TOTAL prompt count and `output` is its TOTAL completion count.
1415
- * The other three counters CLASSIFY a part of one of those totals; none of them adds to it.
1416
- * `cachedInput` and `cacheWriteInput` classify `input`, which is the convention
1417
- * `promptCacheTokenClasses` (`util.ts`) folds: `freshInput = input - cacheRead - cacheWrite`.
1418
- * `reasoningOutput` classifies `output`.
1419
- *
1420
- * A counter the harness does not report stays absent, because a zero would claim the harness
1421
- * measured none.
1422
- */
1423
- interface HarnessUsage {
1424
- /** The harness family whose adapter produced this report. */
1425
- readonly harness: HarnessType;
1426
- /** Total prompt tokens the provider charged for the turn, the cached ones included. */
1427
- readonly input: number;
1428
- /** Total completion tokens the provider charged for the turn, the reasoning ones included. */
1429
- readonly output: number;
1430
- /** The part of `input` the provider served from its prompt cache. */
1431
- readonly cachedInput?: number;
1432
- /** The part of `input` the provider wrote into its prompt cache. */
1433
- readonly cacheWriteInput?: number;
1434
- /** The part of `output` the model spent on reasoning. Never added to `output`. */
1435
- readonly reasoningOutput?: number;
1436
- }
1437
- /**
1438
- * Decode a sandbox event with one harness's adapter, or `undefined` when the event carries no
1439
- * harness-native usage.
1440
- *
1441
- * A NAMED harness reads with that harness's adapter only, and a named harness with no adapter
1442
- * reports nothing. It never falls through to another harness's adapter: a different harness's
1443
- * `turn.completed` decoded as codex would either drop the counters codex does not name or fail on
1444
- * a field codex requires, and both answers would be about the wrong harness. The composite over
1445
- * every registered adapter runs only when the caller cannot name the harness.
1446
- *
1447
- * Throws `ValidationError` when an adapter recognizes the event as its harness's usage carrier and
1448
- * cannot read the numbers.
1449
- */
1450
- declare function decodeHarnessUsage(event: SandboxEvent, harness?: HarnessType): HarnessUsage | undefined;
1451
- //#endregion
1452
- //#region src/runtime/codex-rollout-store.d.ts
1453
- /** Who wrote one rollout, exactly as its own `session_meta` states it. Nothing here is inferred. */
1454
- interface CodexRolloutIdentity {
1455
- /** The rollout's own thread id (`session_meta.payload.id`). */
1456
- readonly sessionId: string;
1457
- /** The thread this one was spawned or forked from, when it was. */
1458
- readonly parentThreadId?: string;
1459
- /** The thread whose rows are prepended into this file, when this file is a fork. */
1460
- readonly forkedFromId?: string;
1461
- /** True when `thread_source` reads `subagent`: a harness-native child, invisible to the journal. */
1462
- readonly nativeChild: boolean;
1463
- /** The child's own path in the harness's agent tree (`/root/c1_b_grid`), when it has one. */
1464
- readonly agentPath?: string;
1465
- /** The harness's own nickname for the child ("Turing"), when it has one. */
1466
- readonly agentNickname?: string;
1467
- /** Spawn depth the harness recorded. `1` is a direct child of the seat. */
1468
- readonly depth?: number;
1469
- /** The working directory the session ran in, used to attribute a store to a workspace. */
1470
- readonly cwd?: string;
1471
- /** The codex build that wrote it. */
1472
- readonly cliVersion?: string;
1473
- /** When the session itself started, from its own `session_meta` timestamp. */
1474
- readonly startedAtMs?: number;
1475
- }
1476
- /** How this reader isolated the session's own rows from the parent rows prepended to its file. */
1477
- type CodexForkBoundary =
1478
- /** Not a fork: every row in the file belongs to this session. */
1479
- {
1480
- readonly kind: 'whole-file';
1481
- } |
1482
- /** A fork whose own first turn was isolated, and by which rule. */
1483
- {
1484
- readonly kind: 'resolved';
1485
- readonly rule: 'history-start-ordinal' | 'turn-is-session' | 'turn-uuid-v7' | 'turn-start-time';
1486
- /** The `turn_id` of the session's own first turn. */
1487
- readonly turnId?: string;
1488
- /** Rows credited to the parent and excluded from `own`. */
1489
- readonly inheritedTurns: number;
1490
- } |
1491
- /** A fork this reader could not isolate. `own` is absent; nothing may be charged. */
1492
- {
1493
- readonly kind: 'unresolved';
1494
- readonly reason: string;
1495
- };
1496
- /** One turn of one session, with the counters it added to the session's cumulative total. */
1497
- interface CodexRolloutTurn {
1498
- readonly turnId?: string;
1499
- readonly startedAtMs?: number;
1500
- readonly usage: HarnessUsage;
1501
- }
1502
- /** One rollout file, read. */
1503
- interface CodexRolloutSession {
1504
- readonly identity: CodexRolloutIdentity;
1505
- readonly boundary: CodexForkBoundary;
1506
- /**
1507
- * The session's OWN spend — the cumulative delta from its fork boundary to its last report.
1508
- * ABSENT when the boundary is unresolved: an unattributable number must not be charged.
1509
- */
1510
- readonly own?: HarnessUsage;
1511
- /** The session's own turns, newest last. Empty when the file reported no usage. */
1512
- readonly turns: readonly CodexRolloutTurn[];
1513
- /**
1514
- * The file's final cumulative `total_token_usage`, kept ONLY as the diagnostic that shows how
1515
- * far a naive file total is from the truth. Never charge this.
1516
- */
1517
- readonly fileCumulativeInput: number;
1518
- readonly fileCumulativeOutput: number;
1519
- }
1520
- /** What one incremental read of a store observed. */
1521
- interface CodexStoreDelta {
1522
- /** Spend by sessions that are NOT native children — the seat's own turns. */
1523
- readonly seat: HarnessUsage;
1524
- /** Spend by `thread_source: subagent` sessions — the harness-native children. */
1525
- readonly native: HarnessUsage;
1526
- /** Sessions whose fork boundary could not be isolated, so their spend is absent, not zero. */
1527
- readonly unresolved: ReadonlyArray<{
1528
- readonly sessionId: string;
1529
- readonly reason: string;
1530
- }>;
1531
- /**
1532
- * Every session this read touched, for evidence. Each one states its WHOLE own spend and turn
1533
- * list, which is not the same number as `seat` / `native`: those two carry only what this read
1534
- * newly observed.
1535
- */
1536
- readonly sessions: readonly CodexRolloutSession[];
1537
- }
1538
- /** A store reader that credits each turn once: it tails only the bytes appended since the last read. */
1539
- interface CodexRolloutStoreReader {
1540
- /**
1541
- * Read everything appended since the previous call and attribute it.
1542
- *
1543
- * The FIRST call establishes the baseline. Call it before the first turn so pre-existing rows are
1544
- * consumed and credited to nothing; every later call returns exactly that turn's spend.
1545
- */
1546
- read(): Promise<CodexStoreDelta>;
1547
- }
1548
- /** Where a harness keeps its own session store, and which workspace may be credited from it. */
1549
- interface CodexRolloutStoreRef {
1550
- /**
1551
- * Absolute path to the harness home the CLI writes into — `CODEX_HOME`, or `$HOME/.codex`.
1552
- * This MUST be the run's own isolated store. Pointing it at an ambient host store credits one
1553
- * run with another run's files, which is the exact defect this reader exists to end.
1554
- */
1555
- readonly root: string;
1556
- /**
1557
- * Credit only sessions whose recorded `cwd` is this path or below it. Absent credits every
1558
- * session under `root`, which is correct only for a store no other run writes to.
1559
- */
1560
- readonly workspaceRoot?: string;
1561
- }
1562
- /**
1563
- * Read one rollout's rows into a session record.
1564
- *
1565
- * `rows` is the file's JSON values in file order. Pass the whole file to read a completed session;
1566
- * the store reader passes appended slices and carries the identity forward itself.
1567
- */
1568
- declare function readCodexRolloutSession(rows: Iterable<unknown>): CodexRolloutSession | undefined;
1569
- /**
1570
- * Open an incremental reader over a codex store.
1571
- *
1572
- * Nothing is read until `read()` is called, and every read is bounded by the bytes appended since
1573
- * the previous one, so a 695MB rollout is scanned once rather than once per turn.
1574
- */
1575
- declare function createCodexRolloutStoreReader(ref: CodexRolloutStoreRef): CodexRolloutStoreReader;
1576
- /** Sum two usage reports on every counter both of them state. */
1577
- declare function addHarnessUsage(left: HarnessUsage, right: HarnessUsage): HarnessUsage;
1578
- /** True when a report states any spend at all. */
1579
- declare function harnessUsageIsEmpty(usage: HarnessUsage): boolean;
1580
- //#endregion
1581
- //#region src/runtime/key-provider.d.ts
1582
- /** Resolve named secrets. The ONE seam every secret store adapts to. */
1583
- interface KeyProvider {
1584
- /** The value for `name`, or `undefined` when this provider does not hold it. */
1585
- get(name: string): Promise<string | undefined>;
1586
- }
1587
- /** The env-backed provider: reads the (dotenvx-loaded) process env. Empty /
1588
- * whitespace-only values count as absent — fail loud, not with a blank key. */
1589
- declare function envKeyProvider(env?: Record<string, string | undefined>): KeyProvider;
1590
- /** The `AgentProfileMcpServer.metadata` key the declarative secret-env map
1591
- * rides under: `{ ENV_VAR_NAME: 'PROVIDER_KEY_NAME' }`. Names only — values
1592
- * are resolved at materialize time and never stored. */
1593
- declare const mcpSecretEnvMetadataKey = "secretEnv";
1594
- /** Read (and validate) a server entry's declared secret-env map, if any.
1595
- * Malformed metadata throws — a half-declared secret must never half-boot. */
1596
- declare function secretEnvOfMcpServer(server: AgentProfileMcpServer): Record<string, string> | undefined;
1597
- /**
1598
- * Resolve a declared secret-env map into the real env entries for a server
1599
- * spawn. Fail-closed: no provider or a missing key throws, naming the KEY
1600
- * NAME only (the value never appears in any message). `label` names the
1601
- * server for the error (e.g. `profile.mcp['exa']`).
1602
- */
1603
- declare function resolveSecretEnv(secretEnv: Record<string, string>, keys: KeyProvider | undefined, label: string): Promise<Record<string, string>>;
1604
- /** The spawn-ready strings for one stdio MCP server: profile config values
1605
- * resolved, secrets separated so the client can redact them. */
1606
- interface ResolvedMcpServerLaunch {
1607
- args?: string[];
1608
- /** Public env, safe to appear in diagnostics. */
1609
- env?: Record<string, string>;
1610
- /** Resolved secret env. Reaches only the child process; redacted everywhere else. */
1611
- protectedEnv?: Record<string, string>;
1612
- }
1613
- /**
1614
- * Resolve a profile MCP server's `args`/`env` config values (interface ≥0.40
1615
- * `AgentProfileConfigValue`) plus the legacy `metadata.secretEnv` channel into
1616
- * the plain strings a spawn needs.
1617
- *
1618
- * Rules, all fail-closed:
1619
- * - `args` must be public values. A secret-ref in argv is refused: argv is
1620
- * readable by every host process (/proc/PID/cmdline) and outside the
1621
- * protected-value redaction channel, so a secret there cannot be contained.
1622
- * - `env` secret-refs resolve through the KeyProvider (missing provider or key
1623
- * throws, naming the KEY NAME only) and land in `protectedEnv`.
1624
- * - An env var declared secret on BOTH channels (env secret-ref and
1625
- * metadata.secretEnv) is ambiguous configuration and throws.
1626
- * - A public `env` entry shadowed by a legacy metadata secret keeps the
1627
- * pre-0.40 spawn precedence: the secret value wins in the child env.
1628
- */
1629
- declare function resolveMcpServerLaunch(server: AgentProfileMcpServer, keys: KeyProvider | undefined, label: string): Promise<ResolvedMcpServerLaunch>;
1630
- //#endregion
1631
- //#region src/runtime/supervise/bridge-config.d.ts
1632
- /**
1633
- * cli-bridge seam. A local OpenAI-compatible bridge that fronts harness CLIs
1634
- * (claude-code / opencode / kimi / pi) behind one HTTP surface. The spawned
1635
- * `AgentProfile` is the sole harness/provider/model and behavioral authority and
1636
- * is forwarded verbatim per request; this seam carries transport data only.
1637
- *
1638
- * The executor opens a resumable cli-bridge session. `sessionId` identifies the
1639
- * harness conversation across turns; each turn also receives its own durable run id.
1640
- * A dropped HTTP reader reattaches to that exact run and explicit cancel is the only
1641
- * operation allowed to stop it. Omit `sessionId` and the executor mints one per spawn.
1642
- *
1643
- * ── HOW TO CONTROL WHAT THE HARNESS LOADS (there is no argv field, by design) ──
1644
- *
1645
- * A worker often needs the harness started in a KNOWN state — no ambient extensions, skills,
1646
- * context files, or prompt templates — because ambient state is how a paired experiment silently
1647
- * loses its pairing: an installed extension that persists memory across runs carries arm A's state
1648
- * into arm B, and nothing reports it.
1649
- *
1650
- * That is what the spawned `AgentProfile` is FOR. `agent_profile`
1651
- * rides every request verbatim, and cli-bridge maps it onto each harness's own native controls:
1652
- *
1653
- * - Materializing any profile at all already starts the harness isolated from ambient
1654
- * workspace state — for pi that is `--no-context-files --no-skills --no-prompt-templates`,
1655
- * applied to every request that carries an `agent_profile`.
1656
- * - `AgentProfile.extensions.<harness>` is the named, per-harness control channel. An explicit
1657
- * `extensions: { pi: { load: [] } }` disables ambient extension discovery outright
1658
- * (pi's `--no-extensions`); listing package names loads exactly those and nothing else.
1659
- * - `permissions` / `tools` / `mcp` map onto the harness's native tool and server controls.
1660
- *
1661
- * A caller therefore does NOT need to hand-roll an `Executor` to isolate a harness run, and the
1662
- * profile expressing it stays portable: the same declaration means the same thing on a different
1663
- * harness, whereas an argv string means nothing anywhere else.
1664
- *
1665
- * WHY NOT A GENERAL ARGV PASSTHROUGH. `bridgeUrl` addresses a process-spawning server. Forwarding
1666
- * an arbitrary argv array to it would let any caller holding a bearer token choose the flags of a
1667
- * process on the bridge host — which for real harness CLIs includes flags that load code from a
1668
- * path, read a file into the prompt, redirect the working directory, or turn off the isolation the
1669
- * bridge applies. cli-bridge deliberately confines workers (a filesystem jail and deny-by-default
1670
- * network egress), and every one of those confinements is expressed as spawn configuration, so an
1671
- * argv channel is a channel for unwinding them. It would also break this executor's own contract:
1672
- * the durable-run replay protocol, session pinning, and streaming mode are all argv the bridge
1673
- * owns, and a caller-supplied duplicate silently wins or corrupts the parse. The structured profile
1674
- * channel is validated, per-harness, portable, and refuses controls it does not understand — keep
1675
- * new harness capability there.
1676
- */
1677
- interface BridgeSeam {
1678
- bridgeUrl: string;
1679
- bridgeBearer: string;
1680
- /**
1681
- * Optional request-scoped model credential.
1682
- *
1683
- * The key name is portable configuration. The provider is a live service and is intentionally
1684
- * not serialised. Runtime resolves both values immediately before every bridge POST and sends
1685
- * them only to a loopback bridge through private request headers.
1686
- */
1687
- modelCredential?: BridgeModelCredential;
1688
- /** Optional working directory forwarded to cli-bridge and persisted with the session. */
1689
- cwd?: string;
1690
- /**
1691
- * The harness's OWN on-disk session store, read as a spend receipt.
1692
- *
1693
- * cli-bridge forwards no token usage for a codex worker, so a turn whose provider counters exist
1694
- * only in codex's rollout meters `{0, 0}` with `tokensKnown: false`. Measured on one live seat
1695
- * (discovery#80): 9 of 9 `metered` events read zero while 27,320,482 codex tokens sat in the same
1696
- * run directory, 1,453,948 of them belonging to harness-native children the journal never saw.
1697
- *
1698
- * Naming the store here turns those rows into evidence. The executor tails it once per turn and
1699
- * credits the DELTA, so each turn is charged once, and it reports the counters with
1700
- * `provenance: 'harness-store'` so a reader can tell a disk receipt from a stream receipt.
1701
- *
1702
- * The path must be the run's OWN isolated store. An ambient host store credits this run with
1703
- * another run's files, and `workspaceRoot` is the structural guard against it.
1704
- */
1705
- harnessStore?: BridgeHarnessStore;
1706
- /** Caller-owned deadline for each bridge turn. Runtime enforces it locally and sends the
1707
- * same value in `execution.timeoutMs` so the bridge-owned process follows the same policy. */
1708
- timeoutMs?: number;
1709
- /** Stable, caller-owned cli-bridge session id for harness-side resume. Defaults
1710
- * to a freshly minted per-spawn id so each worker is its own resumable session. */
1711
- sessionId?: string;
1712
- /** Transport reconnects allowed after the first POST. Default 3; set 0 to disable. */
1713
- maxReconnects?: number;
1714
- /** Newest-last activity window `progress()` reports. Default 12. */
1715
- activityWindow?: number;
1716
- }
1717
- /**
1718
- * A harness's own session store on the bridge host, named so the runtime may read it.
1719
- *
1720
- * Only `codex` has a reader today. Any other harness is REFUSED rather than read with codex's
1721
- * decoder: a different harness's file decoded as a codex rollout would either drop counters it does
1722
- * not name or credit a number that is about the wrong wire shape.
1723
- */
1724
- interface BridgeHarnessStore extends CodexRolloutStoreRef {
1725
- /** The harness family that wrote the store. */
1726
- readonly harness: HarnessType;
1727
- }
1728
- /** A live, request-scoped model credential reference for a local cli-bridge. */
1729
- interface BridgeModelCredential {
1730
- /** Provider key name for the scoped model token. */
1731
- key: string;
1732
- /** Provider key name for the exact scoped HTTPS model gateway URL. */
1733
- baseUrlKey: string;
1734
- /** Live credential service. Runtime retains this reference through reusable captures. */
1735
- provider: KeyProvider;
1736
- }
1737
- //#endregion
1738
- //#region src/runtime/supervise/inbox.d.ts
1739
- /** A message from the run's AUTHORITY — the parent driver. These two kinds carry instruction. */
1740
- interface AuthorityInboxMessage {
1741
- readonly kind: 'steer' | 'answer';
1742
- readonly text: string;
1743
- /** Forceful messages abort the in-flight turn; queued ones wait for the boundary flush. */
1744
- readonly interrupt: boolean;
1745
- /** Present for an `answer` — the question id it resolves. */
1746
- readonly questionId?: string;
1747
- }
1748
- /** A message from a SIBLING worker. Information, never instruction — the parent stays the only
1749
- * authority over this worker's task. */
1750
- interface PeerInboxMessage {
1751
- readonly kind: 'mail';
1752
- readonly text: string;
1753
- /** Always false. Peer mail is queued by construction; see this file's header. */
1754
- readonly interrupt: false;
1755
- readonly envelope: PeerMailEnvelope;
1756
- }
1757
- type InboxMessage = AuthorityInboxMessage | PeerInboxMessage;
1758
- interface Inbox {
1759
- /** The `Executor.deliver` implementation. Returns false when the raw message is malformed and
1760
- * therefore was not queued; callers must not acknowledge a message this inbox discarded. */
1761
- deliver(msg: unknown): boolean;
1762
- /** Resolves at the next accepted delivery, so a manager waiting between turns wakes for it. */
1763
- nextDelivery(): Promise<void>;
1764
- /** Remove and return all pending messages (the flush). */
1765
- drain(): InboxMessage[];
1766
- pending(): number;
1767
- /** Pending messages from the run's AUTHORITY only. This is what the pre-settle fence counts:
1768
- * a worker may not finish while a steer or answer it never read is queued, but peer mail must
1769
- * never be able to hold a finished worker open. */
1770
- pendingAuthority(): number;
1771
- /** Open a fresh per-turn interrupt signal; a later forceful `deliver` aborts it. The loop links
1772
- * this into the signal it passes to its inference call, then re-plans when it fires. */
1773
- freshInterrupt(): AbortSignal;
1774
- /** Render drained messages as ONE operator turn to fold into the worker's conversation. */
1775
- fold(messages: ReadonlyArray<InboxMessage>): string;
1776
- }
1777
- /** Create the worker-side inbox for the down-leg: the driver's `steer_agent` / `answer_question` messages and a sibling's peer mail queue here, and the worker's loop drains them at step boundaries and before settle. */
1778
- declare function createInbox(): Inbox;
1779
- //#endregion
1780
- //#region src/runtime/supervise/sandbox-session.d.ts
1781
- /** Ceiling on continuation turns. Turn 0 is the task; every later turn is a folded steer, so
1782
- * this bounds how many times a supervisor may redirect ONE worker before it must respawn. */
1783
- declare const DEFAULT_SANDBOX_STEERING_MAX_TURNS = 24;
1784
- /** Opt-in configuration for the steerable sandbox worker (`SandboxSeam.steering`). Absent, the
1785
- * sandbox executor keeps its historical single-shot `runAgentRounds` composition verbatim. */
1786
- interface SandboxSteeringOptions {
1787
- /** Max turns for one worker (turn 0 + folded steers). Default {@link DEFAULT_SANDBOX_STEERING_MAX_TURNS}. */
1788
- readonly maxTurns?: number;
1789
- /** How many recent tool/turn notes `progress()` reports. Default 12. */
1790
- readonly activityWindow?: number;
1791
- /** Per-turn wall-clock ceiling; the turn's stream is aborted when it elapses. */
1792
- readonly turnTimeoutMs?: number;
1793
- }
1794
- /** What the steerable session exposes to its executor: the usage stream plus the live reads. */
1795
- interface SteerableSandboxSession {
1796
- /** Drive the worker to settlement. `signal` is the spawn-scoped abort handed to `execute`. */
1797
- stream(task: unknown, signal: AbortSignal): AsyncIterable<UsageEvent>;
1798
- progress(): ExecutorProgress;
1799
- /** Ask the box to stop the running execution on this exact session and report what it answered. */
1800
- cancel(request: ExecutorCancellationRequest): Promise<ExecutorCancellation>;
1801
- traceSource(): TraceSource;
1802
- artifact(): {
1803
- outRef: string;
1804
- out: unknown;
1805
- verdict?: DefaultVerdict;
1806
- spent: Spend;
1807
- } | undefined;
1808
- /** Same output projection used at capture and terminal publication. */
1809
- output(): unknown;
1810
- teardown(): Promise<void>;
1811
- }
1812
- interface SteerableSandboxArgs {
1813
- readonly controller: AbortController;
1814
- readonly profile: AgentProfile;
1815
- readonly harness: BackendType;
1816
- readonly sandboxClient: SandboxClient;
1817
- /** Require capture failures at box cleanup to fail the supervised worker. */
1818
- readonly failOnDestroyError?: boolean;
1819
- readonly workspaceCaptures?: () => ReadonlyArray<ProviderWorkspaceCaptureReceipt>;
1820
- readonly inbox: Inbox;
1821
- readonly taskToPrompt: (task: unknown) => string;
1822
- readonly options?: SandboxSteeringOptions;
1823
- readonly loopCtx?: Partial<Omit<ExecCtx, 'sandboxClient' | 'signal'>>;
1824
- /**
1825
- * Inherited `TRACE_ID` / `PARENT_SPAN_ID` for the box, merged into `CreateSandboxOptions.env` so
1826
- * the remote worker's own spans join the supervisor's trace under the spawning node's span.
1827
- * Absent when the run records no spans — the create options are then untouched.
1828
- */
1829
- readonly traceEnv?: Record<string, string>;
1830
- readonly contentRef: (prefix: string, value: unknown) => string;
1831
- readonly now?: () => number;
1832
- }
1833
- /** One steerable sandbox worker. The returned session is inert until `stream()` is drained. */
1834
- declare function createSteerableSandboxSession(args: SteerableSandboxArgs): SteerableSandboxSession;
1835
- //#endregion
1836
- //#region src/runtime/supervise/runtime.d.ts
1837
- /**
1838
- * Router/inline transport seam. The profile owns model, prompt, and generation behavior.
1839
- */
1840
- interface RouterSeam {
1841
- routerBaseUrl: string;
1842
- routerKey: string;
1843
- /** Injectable transport for offline/local execution; still passes through Runtime metering. */
1844
- complete?: RouterConfig['complete'];
1845
- /** When present, return one turn's requested tool calls without executing them. */
1846
- tools?: ReadonlyArray<ToolSpec>;
1847
- }
1848
- /**
1849
- * Sandbox executor seam. The `sandboxClient` the composed `runAgentRounds` creates
1850
- * boxes through, plus the optional trace/run/lineage wiring forwarded into the
1851
- * loop. `lineage` is opaque here (PR #150's `RunAgentRoundsOptions.lineage`): forwarded
1852
- * forward-compatibly, never inspected — this executor does NOT reinvent
1853
- * checkpoint/fork.
1854
- */
1855
- interface SandboxSeam {
1856
- sandboxClient: SandboxClient;
1857
- /** Surface evidence capture failures from session cleanup. */
1858
- failOnDestroyError?: boolean;
1859
- /** Verified provider captures collected before session teardown. */
1860
- workspaceCaptures?: () => ReadonlyArray<ProviderWorkspaceCaptureReceipt>;
1861
- /** Forwarded into the composed `runAgentRounds`'s `ctx` (trace emitter, run handle, etc.). */
1862
- loopCtx?: Partial<Omit<ExecCtx, 'sandboxClient' | 'signal'>>;
1863
- /** PR #150 `RunAgentRoundsOptions.lineage` passthrough — opaque; forwarded, not parsed. */
1864
- lineage?: unknown;
1865
- /** Hard cap on the composed loop's iterations. The budget pool reserves against
1866
- * the spawn `Budget.maxIterations`; this is the leaf's own ceiling. Default 1. */
1867
- maxIterations?: number;
1868
- /**
1869
- * OPT-IN executable score for this worker. Forwarded to the composed
1870
- * `runAgentRounds` as its `validator`, so the kernel calls `validate` while the
1871
- * iteration's box is still alive: `ValidationCtx.box` is a LIVE `SandboxInstance`
1872
- * and the check can run commands or read files in the container it is scoring.
1873
- * Every other supervised hook fires after teardown and can only read the artifact.
1874
- *
1875
- * The resulting verdict becomes the winner's verdict, which this executor already
1876
- * surfaces on its `ExecutorResult`. Absent, nothing changes: the loop runs
1877
- * unscored and the leaf falls back to its own settle verdict.
1878
- *
1879
- * Not representable with `steering` — a steerable session is a multi-turn session
1880
- * on one box, not a `runAgentRounds` composition, so the pair is rejected instead
1881
- * of silently dropping the score.
1882
- */
1883
- validator?: Validator<SandboxLeafOut>;
1884
- /**
1885
- * OPT-IN: run this worker as a multi-turn, STEERABLE session instead of the historical
1886
- * single-shot `runAgentRounds` composition. Setting it gives the sandbox worker an `Executor.deliver`
1887
- * inbox (so `Scope.send` / `steer_agent` actually reach it), a live tool-activity trace, and a
1888
- * `progress()` read — turning the default cloud worker from something a supervisor can only
1889
- * wait on into something it can watch and correct.
1890
- *
1891
- * Absent, nothing changes: the same `runAgentRounds` leaf, no inbox, `steer_agent` still reports
1892
- * `delivered:false`. Opt-in because a steerable worker holds ONE box across several turns,
1893
- * which is a different resource profile from a fire-and-forget shot.
1894
- */
1895
- steering?: SandboxSteeringOptions;
1896
- }
1897
- /**
1898
- * UNMETERED CLI subprocess seam. `bin` + `args` describe the process to spawn.
1899
- *
1900
- * READ THIS BEFORE CHOOSING `backend: 'cli'`. This backend pipes a prompt to a subprocess's stdin
1901
- * and reads its stdout. It has no usage receipt of any kind, so it reports its spend with
1902
- * `Spend.tokensKnown: false`: the work is recorded, its `{0,0}` tokens and `$0` are a FLOOR rather
1903
- * than a measurement, and a ceiling priced from either is a ceiling that cannot fire. The executor
1904
- * is also `budgetExempt: true`, which is why `driveHarnessFromBackend` refuses it outright rather
1905
- * than pretending to budget it.
1906
- *
1907
- * If you need a metered harness worker, use `backend: 'bridge'` (a cli-bridge session, which
1908
- * reports the harness's real per-turn tokens and cost) or `backend: 'cli-worktree'` with
1909
- * `codexReproducible`. Reach for this seam only when the subprocess genuinely is not an inference
1910
- * agent, or when you have accepted that its cost is invisible.
1911
- *
1912
- * `args` is argv for a LOCAL, in-process spawn under this process's own privileges. It is not a
1913
- * remote channel and nothing forwards it over a wire.
1914
- */
1915
- interface CliSeam {
1916
- bin: string;
1917
- args?: string[];
1918
- /** Extra environment for the subprocess (merged over `process.env`). */
1919
- env?: Record<string, string>;
1920
- /** Working directory for the subprocess. */
1921
- cwd?: string;
1922
- }
1923
- /**
1924
- * cli-worktree seam. A supervisor-authored `AgentProfile` driving a local coding-harness CLI
1925
- * (claude / codex / opencode) on its own git worktree — the leaf `createWorktreeCliExecutor`
1926
- * named as data. `repoRoot` is transport data; `AgentProfile.harness` selects the CLI.
1927
- * `taskPrompt` remains an optional direct-call fallback for callers that execute with `undefined`.
1928
- * The authored
1929
- * `profile.prompt.systemPrompt` + `profile.model.default` reach the harness via the §1.5
1930
- * `harnessInvocation` mapper. Everything else mirrors `WorktreeCliExecutorOptions`.
1931
- */
1932
- interface CliWorktreeSeam {
1933
- repoRoot: string;
1934
- taskPrompt?: string;
1935
- runId?: string;
1936
- baseRef?: string;
1937
- harnessTimeoutMs?: number;
1938
- /** Isolated, network-off Codex execution with terminal JSONL usage capture. */
1939
- codexReproducible?: boolean;
1940
- /** Absolute host paths denied to reproducible Codex. */
1941
- codexReadDeniedPaths?: ReadonlyArray<string>;
1942
- testCmd?: string;
1943
- typecheckCmd?: string;
1944
- checkTimeoutMs?: number;
1945
- checkOutputCap?: number;
1946
- budgetExempt?: boolean;
1947
- /** Live cli-bridge transport inside the worktree. When set, the worktree leaf accepts
1948
- * `deliver()` messages and resumes the same bridge session in this worktree cwd. */
1949
- bridge?: CliWorktreeBridgeSeam;
1950
- /** Test seam — forwarded to worktree helpers. */
1951
- runGit?: GitRunner;
1952
- /** Test seam — forwarded to verification checks. */
1953
- runCommand?: WorktreeCheckRunner;
1954
- }
1955
- /**
1956
- * cli-in-place seam. A supervisor-authored `AgentProfile` driving a local coding-harness CLI
1957
- * (claude-code / codex / opencode / pi) on a workspace the CALLER supplies — the leaf
1958
- * `createInPlaceCliExecutor` named as data. `workspacePath` is transport data;
1959
- * `AgentProfile.harness` selects the CLI, and the authored `profile.prompt.systemPrompt` +
1960
- * `profile.model.default` reach the harness via the §1.5 `harnessInvocation` mapper.
1961
- *
1962
- * READ THIS BEFORE CHOOSING BETWEEN THIS AND `cli-worktree`. They differ in ONE thing, and it is
1963
- * the thing that decides which one a caller wants:
1964
- *
1965
- * - `cli-worktree` cuts a git worktree of its OWN off `repoRoot`, runs the harness there,
1966
- * returns the captured patch, and removes the worktree at teardown. The directory it was
1967
- * given is never edited. That is correct for a fanout of N candidate authors that must not
1968
- * clobber each other, and for a caller whose deliverable IS the patch.
1969
- * - `cli-in-place` runs the harness in `workspacePath` itself. The edits stay in that directory
1970
- * after the call, so the NEXT call sees them. That is what a caller needs when the workspace
1971
- * has to persist between calls — a multi-shot author resuming on top of its own edits
1972
- * (`agenticGenerator`), or a candidate directory the caller commits itself.
1973
- *
1974
- * Because the workspace persists, so would the profile inputs this path materializes into it. They
1975
- * are removed before the call returns, so the directory a caller inspects afterwards holds the
1976
- * harness's own edits and nothing else, and a `git status` over it answers "did the author change
1977
- * anything" rather than "did Runtime write a settings file".
1978
- *
1979
- * There is no reproducible-Codex mode here: that mode stages an executable and a write probe INTO
1980
- * its working directory, which a caller-owned workspace is not the place for. Use `cli-worktree`
1981
- * with `codexReproducible` when the isolated, metered Codex run is what you want.
1982
- */
1983
- interface CliInPlaceSeam {
1984
- /** Absolute path to the EXISTING directory the harness edits. Runtime never creates, cleans, or
1985
- * removes it. */
1986
- workspacePath: string;
1987
- taskPrompt?: string;
1988
- harnessTimeoutMs?: number;
1989
- /** Test seam — inject the harness runner so unit tests script a `LocalHarnessResult`. */
1990
- runHarness?: typeof runLocalHarness;
1991
- }
1992
- interface CliWorktreeBridgeSeam {
1993
- bridgeUrl: string;
1994
- bridgeBearer: string;
1995
- /** Caller-owned deadline for each bridge turn. Runtime enforces it locally and sends the
1996
- * same value in `execution.timeoutMs` so cli-bridge cannot substitute its own cutoff. */
1997
- timeoutMs?: number;
1998
- /** Stable cli-bridge session id. Defaults to `bridge-worktree-${runId}`. */
1999
- sessionId?: string;
2000
- /** Transport reconnects allowed after the first POST. Default 3; set 0 to disable. */
2001
- maxReconnects?: number;
2002
- }
2003
- /**
2004
- * Generic environment provider executor config. External packages implement
2005
- * `AgentEnvironmentProvider`; this built-in wrapper lets `createExecutor` consume them as backend
2006
- * data while preserving the existing usage channel. Runtime depends on no provider package, so a
2007
- * Tangle provider and a hand-written one compose identically. Worked wiring:
2008
- * `examples/provider-executor/`.
2009
- *
2010
- * Everything a create needs travels on `CreateAgentEnvironmentInput` through
2011
- * {@link ProviderExecutorOptions.defaults}; everything one turn needs travels on
2012
- * {@link ProviderExecutorOptions.promptOptions}. Wrapping the provider's own client to reach a
2013
- * field is what this seam exists to replace: the wrapper is invisible to Runtime, so its options
2014
- * are absent from every record the run produces.
2015
- *
2016
- * READINESS IS THE PROVIDER'S CONTRACT. `provider.create` resolves with an environment that can
2017
- * take a turn, so this seam streams straight into it and adds no readiness wait of its own. The
2018
- * sandbox seam's `acquireSandbox` exists because a raw `SandboxClient.create` returns before the
2019
- * box is ready; a second poll here would hide a provider that does not honor the contract, and
2020
- * that provider is an upstream defect to report rather than a race to paper over.
2021
- */
2022
- interface ProviderSeam extends ProviderExecutorOptions {
2023
- provider: AgentEnvironmentProvider$1 | string;
2024
- registry?: AgentEnvironmentProviderRegistry;
2025
- /**
2026
- * Compose the provider through the existing steerable sandbox session.
2027
- * The exact profile must name its harness, and the provider must expose live
2028
- * continuation plus session controls. The provider still owns environment
2029
- * creation and session semantics.
2030
- */
2031
- steering?: SandboxSteeringOptions;
2032
- /**
2033
- * Place each worker whose profile a shared box can carry as its own process in a pool of shared
2034
- * boxes, and keep a dedicated environment from `provider` for every other profile. Build it with
2035
- * `sharedBoxPlacement`. A manager never uses it: its coordination credential is create-time
2036
- * environment that every co-tenant of a shared box could read.
2037
- */
2038
- shared?: SharedBoxPlacement;
2039
- }
2040
- /**
2041
- * Router seam WITH tool use — the tool-using router backend. Same direct
2042
- * OpenAI-compatible endpoint as `RouterSeam`, but each turn passes `tools`; when
2043
- * the model emits tool_calls they run via `executeToolCall` ON THIS HOST and the
2044
- * results fold back as `tool` messages, repeating until the model answers without
2045
- * a tool or `maxTurns` is hit. A real agentic loop, OFF-BOX — no sandbox, so it
2046
- * is unaffected by a box's egress allowlist. One turn = one completion = the
2047
- * equal-compute unit. `executeToolCall` receives the task so per-task tool
2048
- * surfaces (e.g. a gym keyed by task) can dispatch correctly.
2049
- */
2050
- interface RouterToolsSeam {
2051
- routerBaseUrl: string;
2052
- routerKey: string;
2053
- complete?: RouterConfig['complete'];
2054
- tools: ReadonlyArray<ToolSpec>;
2055
- executeToolCall: (name: string, args: Record<string, unknown>, task: unknown) => Promise<string>;
2056
- /** Exact conversation to continue. Runtime validates its system message against the profile. */
2057
- initialMessages?: ReadonlyArray<Readonly<Record<string, unknown>>>;
2058
- /** Persist each buffered HTTP attempt, including failed responses, before the next turn. */
2059
- onProviderResponse?: (receipt: RouterResponseReceipt) => void | Promise<void>;
2060
- /** Observe the detached final conversation for session persistence. */
2061
- onMessages?: (messages: ReadonlyArray<Readonly<Record<string, unknown>>>) => void | Promise<void>;
2062
- /** Online observer of each tool step — the seam a `DetectorMonitor` taps to watch the live pipe
2063
- * (raise a `finding` when the worker loops/errors). Called after every tool call resolves, with
2064
- * real per-call wall-clock (`startedAt`/`endedAt`/`durationMs`) so a push `TraceSource` can carry
2065
- * non-zero span durations onto the unified timeline. */
2066
- onToolStep?: (step: {
2067
- toolName: string;
2068
- args: Record<string, unknown>;
2069
- status: 'ok' | 'error';
2070
- startedAt?: number;
2071
- endedAt?: number;
2072
- durationMs?: number;
2073
- }) => void;
2074
- }
2075
- /**
2076
- * The leaf `createWorktreeCliExecutor` as a backend-as-data factory: a supervisor-authored
2077
- * `AgentProfile` driving claude / codex / opencode on its own worktree. `budgetExempt` like
2078
- * the other CLI leaves; the authored systemPrompt + model reach the harness via §1.5.
2079
- */
2080
- declare const cliWorktreeExecutor: ExecutorFactory<unknown>;
2081
- /**
2082
- * The leaf `createInPlaceCliExecutor` as a backend-as-data factory: a supervisor-authored
2083
- * `AgentProfile` driving a local coding CLI in the workspace the caller supplied, so its edits are
2084
- * still there for the next spawn. `budgetExempt` like the other CLI leaves; the authored
2085
- * systemPrompt + model reach the harness via §1.5.
2086
- */
2087
- declare const cliInPlaceExecutor: ExecutorFactory<unknown>;
2088
- /**
2089
- * Config for {@link createExecutor}: the backend is DATA — the cost dial a profile,
2090
- * an experiment config, or a replay journal can name — not an import choice. Each
2091
- * variant carries its backend's seam.
2092
- */
2093
- type ExecutorConfig = ({
2094
- backend: 'router';
2095
- } & RouterSeam) | ({
2096
- backend: 'router-tools';
2097
- } & RouterToolsSeam) | ({
2098
- backend: 'bridge';
2099
- } & BridgeSeam) | ({
2100
- backend: 'cli';
2101
- } & CliSeam) | ({
2102
- backend: 'cli-worktree';
2103
- } & CliWorktreeSeam) | ({
2104
- backend: 'cli-in-place';
2105
- } & CliInPlaceSeam) | ({
2106
- backend: 'provider';
2107
- } & ProviderSeam) | ({
2108
- backend: 'sandbox';
2109
- } & SandboxSeam);
2110
- /**
2111
- * The single built-in executor factory. Picks a leaf backend by data (`config.backend`),
2112
- * injects the matching seam, and delegates to that backend's built-in implementation.
2113
- * The `Executor` port stays OPEN: bring-your-own agents implement `Executor` directly, while Scope
2114
- * or `createExecutorRegistry` still parses and seals their exact profile before use. Use this instead of a
2115
- * per-vendor adapter or a closed `inline|sandbox|cli` switch — those bypass the
2116
- * `UsageEvent` reporting channel.
2117
- */
2118
- declare function createExecutor(config: ExecutorConfig): ExecutorFactory<unknown>;
2119
- /**
2120
- * The open resolver/registry. Pre-registers the three built-ins under their
2121
- * runtime tags (`'router'`, `'sandbox'`, `'cli'`) and accepts `register(name,
2122
- * factory)` for any additional runtime. A BYO `AgentSpec.executor` has highest routing precedence
2123
- * after the same exact-profile intake validation. Registration + BYO remain open extension points.
2124
- *
2125
- * `resolve` precedence (frozen in `ExecutorRegistry`): a BYO `spec.executorFactory` →
2126
- * `spec.executor` → `harness === null` → the `'router'` factory; else a registered factory for the
2127
- * harness-derived runtime (`'sandbox'` for any `BackendType`); else fail loud.
2128
- */
2129
- declare function createExecutorRegistry(): ExecutorRegistry;
2130
- //#endregion
2131
- //#region src/runtime/stream-agent-turn.d.ts
2132
- /**
2133
- * The execution substrate one turn runs on — a closed discriminated union over
2134
- * the three stream surfaces the runtime already owns.
2135
- *
2136
- * @stable
2137
- */
2138
- type AgentTurnBackend = {
2139
- /** A Runtime-owned executor factory materialized from this exact canonical profile. */
2140
- kind: 'executor';
2141
- factory: ExecutorFactory<unknown>;
2142
- /** Exact canonical identity materialized by the executor. */
2143
- profile: AgentProfile;
2144
- /** Model label stamped on cost-only `llm_call` events. Default `'agent'`. */
2145
- agentRunName?: string;
2146
- };
2147
- /** @stable */
2148
- interface StreamAgentTurnOptions {
2149
- /** Caller-initiated cancellation. Terminates the stream with `final.status: 'aborted'`. */
2150
- signal?: AbortSignal;
2151
- /**
2152
- * Wall-clock deadline for the whole turn in ms. An expired deadline aborts
2153
- * the backend and terminates the stream with `final.status: 'failed'`
2154
- * (a blown deadline is a turn failure, not a caller cancellation).
2155
- */
2156
- timeoutMs?: number;
2157
- /** Stable logical paid-call id, forwarded as the provider idempotency key and retained in evidence. */
2158
- callId?: string;
2159
- /** Caller trace tag retained in evidence and forwarded when the transport supports it. */
2160
- correlationId?: string;
2161
- /**
2162
- * Opt-in tool-part projection for box and executor backends: sandbox tool
2163
- * parts additionally surface in-stream as
2164
- * `tool_call` / `tool_result` events (`mapSandboxToolEvent`), so a consumer
2165
- * rendering tool activity needs no bespoke sandbox-event parser. Default
2166
- * off — the stream vocabulary existing consumers see is unchanged. No-op
2167
- * for the `chat` kind (its backend emits `RuntimeStreamEvent`s directly,
2168
- * tool events included when the backend produces them).
2169
- */
2170
- preserveToolParts?: boolean;
2171
- /**
2172
- * Raw-event tap for box-kind backends: called (and awaited) with every
2173
- * unmapped `SandboxEvent` BEFORE it is projected, so a consumer can read
2174
- * parts the chat-UX projection drops (part ids, step markers, custom
2175
- * backend events) without forking the mapper. Purely observational — it
2176
- * cannot alter the mapped stream. Never called for the `chat` kind, which
2177
- * has no sandbox events.
2178
- */
2179
- onRawEvent?: (event: SandboxEvent) => void | Promise<void>;
2180
- }
2181
- /**
2182
- * Metered usage of one turn, summed over every cost-bearing event the backend
2183
- * emitted. `input`/`output` are token counts and are accompanied by
2184
- * `tokensKnown: false` when the backend did not report them. `costUsd`/`model`
2185
- * are present only when the backend actually reported them.
2186
- *
2187
- * @stable
2188
- */
2189
- interface AgentTurnUsage {
2190
- input: number;
2191
- output: number;
2192
- /** Present when a real turn ran but the provider did not report token usage. */
2193
- tokensKnown?: false;
2194
- costUsd?: number;
2195
- /** Present when Runtime could not prove the full dollar amount. */
2196
- usdKnown?: false;
2197
- /** Separately-labelled local/catalog estimate; never billed spend. */
2198
- estimatedCostUsd?: number;
2199
- /** Provider-reported prompt-cache fields; absent fields remain unknown. */
2200
- promptCache?: Readonly<Record<string, number | string>>;
2201
- /** Provider-reported reasoning-token subset of output, when available. */
2202
- reasoningTokens?: number;
2203
- model?: string;
2204
- }
2205
- /**
2206
- * A drained turn: the terminal summary plus every event the stream yielded.
2207
- * `status`/`error` mirror the terminal `final` event so a failed or aborted
2208
- * turn stays inspectable without re-scanning `events`.
2209
- *
2210
- * @stable
2211
- */
2212
- interface CollectedAgentTurn {
2213
- finalText: string;
2214
- /** Exact terminal artifact output from a Runtime-owned executor. */
2215
- output?: unknown;
2216
- usage: AgentTurnUsage;
2217
- /** Exact underlying transport calls when the Runtime-owned executor reports them. */
2218
- transportAttempts?: number;
2219
- toolCalls: Array<{
2220
- id?: string;
2221
- name: string;
2222
- arguments: string;
2223
- }>;
2224
- events: RuntimeStreamEvent[];
2225
- status: AgentTaskStatus;
2226
- error?: BackendErrorDetail;
2227
- /** Public Sandbox outcome, when the turn ran through a Sandbox stream or executor. */
2228
- sandboxOutcome?: AgentRunOutcome;
2229
- }
2230
- /**
2231
- * Run ONE agent turn on any backend kind and stream its events. Yields the
2232
- * `RuntimeStreamEvent` vocabulary incrementally and always ends with a `final`
2233
- * event carrying the turn's text and usage (`metadata.tokenUsage`,
2234
- * `metadata.costUsd?`, `metadata.model?`) — on success, failure, abort, and
2235
- * timeout alike. The generator never throws; failures surface in-band as
2236
- * `backend_error` + `final` with a typed `error` detail.
2237
- *
2238
- * @stable
2239
- */
2240
- declare function streamAgentTurn(backend: AgentTurnBackend, input: AgentTurnInput, opts?: StreamAgentTurnOptions): AsyncGenerator<RuntimeStreamEvent>;
2241
- /**
2242
- * Drain a `streamAgentTurn` stream (or any `RuntimeStreamEvent` stream that
2243
- * honors its terminal contract) into the turn summary plus the full event
2244
- * list. Fail-loud: throws when the stream ends without a terminal `final`
2245
- * event — a stream that violates the contract must not read as an empty turn.
2246
- *
2247
- * @stable
2248
- */
2249
- declare function collectAgentTurn(stream: AsyncIterable<RuntimeStreamEvent>): Promise<CollectedAgentTurn>;
2250
- //#endregion
2251
- export { decodeHarnessUsage as $, PeerMailSendInput as $n, WorkspaceRequest as $t, createInbox as A, harnessSupportsReasoningEffort as An, AgentEnvironmentProvider$1 as At, secretEnvOfMcpServer as B, ToolLoopCompactionOptions as Bn, AgentTurnResult$2 as Bt, SteerableSandboxArgs as C, CodexExecutionPolicy as Cn, mapSandboxToolEvent as Ct, Inbox as D, LocalHarness as Dn, AgentEnvironment$1 as Dt, AuthorityInboxMessage as E, LOCAL_HARNESSES as En, sumSandboxUsage as Et, ResolvedMcpServerLaunch as F, RouterTransportConfig as Fn, AgentEnvironmentSummary as Ft, CodexRolloutStoreRef as G, PEER_MAIL_WIRE_KEY as Gn, ExecResult as Gt, CodexRolloutIdentity as H, ToolLoopToolCall as Hn, CheckpointRequest as Ht, envKeyProvider as I, ToolSpec as In, AgentProfileRef as It, addHarnessUsage as J, PeerMailKind as Jn, ProviderAsSandboxClientOptions as Jt, CodexRolloutTurn as K, PeerMailEnvelope as Kn, ForkRequest as Kt, mcpSecretEnvMetadataKey as L, ToolLoopCallContext as Ln, AgentSession as Lt, BridgeModelCredential as M, parseCodexTokenUsage as Mn, AgentEnvironmentProviderRegistry as Mt, BridgeSeam as N, runLocalHarness as Nn, AgentEnvironmentQuery as Nt, InboxMessage as O, LocalHarnessResult as On, AgentEnvironmentCapabilities$1 as Ot, KeyProvider as P, RouterResponseReceipt as Pn, AgentEnvironmentStatus as Pt, HarnessUsage as Q, PeerMailRefusal as Qn, ResourceRequest as Qt, resolveMcpServerLaunch as R, ToolLoopChat as Rn, AgentSessionRef as Rt, SandboxSteeringOptions as S, CodexExecutionEvidence as Sn, mapSandboxEvent as St, createSteerableSandboxSession as T, DEFAULT_LOCAL_HARNESS as Tn, sandboxProgressEvents as Tt, CodexRolloutSession as U, AUTHORITY_MARKERS as Un, CreateAgentEnvironmentInput$1 as Ut, CodexForkBoundary as V, ToolLoopMessageRecord as Vn, CheckpointRef as Vt, CodexRolloutStoreReader as W, DEFAULT_PEER_MAIL_LIMITS as Wn, ExecRequest as Wt, harnessUsageIsEmpty as X, PeerMailOutcome as Xn, ProviderLeafOut as Xt, createCodexRolloutStoreReader as Y, PeerMailLimits as Yn, ProviderExecutorOptions as Yt, readCodexRolloutSession as Z, PeerMailReadout as Zn, ProviderPromptOptions as Zt, cliInPlaceExecutor as _, RemoveWorktreeOptions as _n, SandboxUsageLedger as _t, StreamAgentTurnOptions as a, SandboxControlClient as an, peerMailTools as ar, SharedBoxPlacement as at, createExecutorRegistry as b, createWorktree as bn, createSandboxUsageLedger as bt, CliInPlaceSeam as c, InPlaceHarnessResult as cn, JsonRpcResponse as cr, SharedBoxStats as ct, CliWorktreeSeam as d, WorktreeHarnessResult as dn, McpTransport as dr, sharedBoxRefusal as dt, createAgentEnvironmentProviderRegistry as en, PeerMailbox as er, DEFAULT_SHARED_BOX_RESOURCES as et, ExecutorConfig as f, WorktreeProfileMaterializationReceipt as fn, Redactor as fr, sharedWorkerClientName as ft, SandboxSeam as g, GitRunner as gn, SandboxToolPartState as gt, RouterToolsSeam as h, DiffResult as hn, SandboxServedBackend as ht, CollectedAgentTurn as i, CreateTangleSandboxExactProcessProviderOptions as in, isPeerMailEnvelope as ir, SharedBoxHandle as it, BridgeHarnessStore as j, localHarnessExecutable as jn, AgentEnvironmentProviderRef as jt, PeerInboxMessage as k, RunLocalHarnessOptions as kn, AgentEnvironmentEvent$1 as kt, CliSeam as l, WorktreeCheckRunner as ln, McpToolAnnotations as lr, SharedWorkerIdentity as lt, RouterSeam as m, DiffOptions as mn, resolveRedactor as mr, SandboxOutputMarker as mt, AgentTurnInput$1 as n, providerAsSandboxClient as nn, claimsAuthority as nr, ROUTER_CLIENT_HEADER as nt, collectAgentTurn as o, createTangleSandboxExactProcessProvider as on, peerMailVerbNames as or, SharedBoxPlacementOptions as ot, ProviderSeam as p, CreateWorktreeOptions as pn, defaultRedactor as pr, SandboxLeafOut as pt, CodexStoreDelta as q, PeerMailEvent as qn, PlacementInfo as qt, AgentTurnUsage as r, resolveAgentEnvironmentProvider as rn, createPeerMailbox as rr, SharedBoxCloseReceipt as rt, streamAgentTurn as s, ProviderPlacement as sn, JsonRpcMessage as sr, SharedBoxProcess as st, AgentTurnBackend as t, providerAsExecutor as tn, PeerMailboxOptions as tr, DEFAULT_SHARED_BOX_WORKERS as tt, CliWorktreeBridgeSeam as u, WorktreeCommandResult as un, McpToolDescriptor as ur, sharedBoxPlacement as ut, cliWorktreeExecutor as v, WorktreeHandle as vn, assertSandboxServedModel as vt, SteerableSandboxSession as w, CodexTokenUsage as wn, sandboxEventServedBackend as wt, DEFAULT_SANDBOX_STEERING_MAX_TURNS as x, removeWorktree as xn, extractLlmCallEvent as xt, createExecutor as y, captureWorktreeDiff as yn, createSandboxToolPartState as yt, resolveSecretEnv as z, ToolLoopCompaction as zn, AgentSessionStatus$1 as zt };
2252
- //# sourceMappingURL=stream-agent-turn-D-zmKgU2.d.ts.map