@tangle-network/agent-runtime 0.107.5 → 0.108.1

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 (63) hide show
  1. package/README.md +17 -4
  2. package/dist/{activation-Ck5ksFWg.js → activation-DRpnplEm.js} +3 -3
  3. package/dist/{activation-Ck5ksFWg.js.map → activation-DRpnplEm.js.map} +1 -1
  4. package/dist/agent.d.ts +1 -1
  5. package/dist/agent.js +2 -2
  6. package/dist/candidate-execution/index.js +4 -4
  7. package/dist/{candidate-execution-BUpR0mSD.js → candidate-execution-nYMa-bc-.js} +4 -4
  8. package/dist/{candidate-execution-BUpR0mSD.js.map → candidate-execution-nYMa-bc-.js.map} +1 -1
  9. package/dist/durable.d.ts +134 -0
  10. package/dist/durable.js +124 -0
  11. package/dist/durable.js.map +1 -0
  12. package/dist/{environment-provider-IUGU3epE.d.ts → environment-provider-D0NXc4Qz.d.ts} +73 -4
  13. package/dist/environment-provider.d.ts +1 -1
  14. package/dist/{improvement-cycle-Ulvvbg5p.js → improvement-cycle-CRnDDdX0.js} +49 -155
  15. package/dist/improvement-cycle-CRnDDdX0.js.map +1 -0
  16. package/dist/{index-B3wAV1SJ.d.ts → index-BZbJWqoZ.d.ts} +165 -25
  17. package/dist/{index-BbB5mmK0.d.ts → index-DQp3BPeC.d.ts} +11 -159
  18. package/dist/{index-D2g5qCwg.d.ts → index-cdkwHruJ.d.ts} +3 -3
  19. package/dist/index.d.ts +6 -6
  20. package/dist/index.js +14 -121
  21. package/dist/index.js.map +1 -1
  22. package/dist/intelligence.d.ts +15 -7
  23. package/dist/intelligence.js +4 -4
  24. package/dist/{knowledge-Dizh_AtL.js → knowledge-rdrpPIXs.js} +5 -5
  25. package/dist/{knowledge-Dizh_AtL.js.map → knowledge-rdrpPIXs.js.map} +1 -1
  26. package/dist/knowledge.d.ts +1 -1
  27. package/dist/knowledge.js +1 -1
  28. package/dist/{loop-runner-bin-Dw9_OZfu.d.ts → loop-runner-bin-9-JlGqN7.d.ts} +3 -3
  29. package/dist/{loop-runner-bin-BHELvjdO.js → loop-runner-bin-B1V-XS5A.js} +3 -3
  30. package/dist/{loop-runner-bin-BHELvjdO.js.map → loop-runner-bin-B1V-XS5A.js.map} +1 -1
  31. package/dist/loop-runner-bin.d.ts +1 -1
  32. package/dist/loop-runner-bin.js +1 -1
  33. package/dist/loops.d.ts +3 -3
  34. package/dist/loops.js +5 -5
  35. package/dist/mcp/bin.js +1 -1
  36. package/dist/mcp/index.d.ts +1 -1
  37. package/dist/mcp/index.js +4 -4
  38. package/dist/{openai-tools-CKLy1C7M.js → openai-tools-BjQq2TTL.js} +2 -2
  39. package/dist/{openai-tools-CKLy1C7M.js.map → openai-tools-BjQq2TTL.js.map} +1 -1
  40. package/dist/{prepare-_WTTffkz.js → prepare-Pr19X93k.js} +141 -6
  41. package/dist/prepare-Pr19X93k.js.map +1 -0
  42. package/dist/primeintellect/index.d.ts +1 -1
  43. package/dist/{protected-model-port-ZUwVuftA.js → protected-model-port-uthtDNqu.js} +2 -2
  44. package/dist/{protected-model-port-ZUwVuftA.js.map → protected-model-port-uthtDNqu.js.map} +1 -1
  45. package/dist/{redact-C5xOm8cu.d.ts → redact-BkasKlyd.d.ts} +17 -27
  46. package/dist/{runtime-M_GIzIwe.js → runtime-CDekUROm.js} +4 -4
  47. package/dist/{runtime-M_GIzIwe.js.map → runtime-CDekUROm.js.map} +1 -1
  48. package/dist/{structural-rollout-IXUEplky.js → structural-rollout-BC81Otmc.js} +10 -9
  49. package/dist/structural-rollout-BC81Otmc.js.map +1 -0
  50. package/dist/{supervise-BiRutHS9.js → supervise-JfKPwIlO.js} +270 -69
  51. package/dist/supervise-JfKPwIlO.js.map +1 -0
  52. package/dist/{supervisor-DTKhF-RV.js → supervisor-B2LzaWRb.js} +221 -16
  53. package/dist/supervisor-B2LzaWRb.js.map +1 -0
  54. package/dist/testing.js +288 -239
  55. package/dist/testing.js.map +1 -1
  56. package/dist/{workspace-archive-CIG1VNCj.js → workspace-archive-C9w_rxt6.js} +2 -2
  57. package/dist/{workspace-archive-CIG1VNCj.js.map → workspace-archive-C9w_rxt6.js.map} +1 -1
  58. package/package.json +11 -6
  59. package/dist/improvement-cycle-Ulvvbg5p.js.map +0 -1
  60. package/dist/prepare-_WTTffkz.js.map +0 -1
  61. package/dist/structural-rollout-IXUEplky.js.map +0 -1
  62. package/dist/supervise-BiRutHS9.js.map +0 -1
  63. package/dist/supervisor-DTKhF-RV.js.map +0 -1
@@ -0,0 +1,124 @@
1
+ //#region src/durable/chat-engine.ts
2
+ const encoder = new TextEncoder();
3
+ function encodeLine(event) {
4
+ return encoder.encode(`${JSON.stringify(event)}\n`);
5
+ }
6
+ function defaultLog(message, meta) {
7
+ if (meta) console.error(message, meta);
8
+ else console.error(message);
9
+ }
10
+ /**
11
+ * Run one chat turn. Returns immediately with a `ReadableStream` body;
12
+ * execution starts while the stream is constructed. Backend
13
+ * failures surface as `error` + `session.run.failed` events.
14
+ */
15
+ function handleChatTurn(input) {
16
+ const log = input.log ?? defaultLog;
17
+ const { identity, hooks } = input;
18
+ return {
19
+ body: new ReadableStream({ start: async (controller) => {
20
+ const emit = async (event) => {
21
+ controller.enqueue(encodeLine(event));
22
+ if (hooks.onEvent) try {
23
+ await hooks.onEvent(event);
24
+ } catch (err) {
25
+ log("[chat-engine] onEvent hook threw", { error: err instanceof Error ? err.message : String(err) });
26
+ }
27
+ };
28
+ try {
29
+ await emit({
30
+ type: "session.run.started",
31
+ data: {
32
+ sessionId: identity.sessionId,
33
+ tenantId: identity.tenantId,
34
+ turnIndex: identity.turnIndex
35
+ }
36
+ });
37
+ const producer = hooks.produce();
38
+ for await (const event of producer.stream) await emit(event);
39
+ const rawFinal = producer.finalText();
40
+ const finalText = hooks.transformFinalText ? await hooks.transformFinalText(rawFinal) : rawFinal;
41
+ await hooks.persistAssistantMessage({
42
+ identity,
43
+ finalText
44
+ });
45
+ if (hooks.onTurnComplete) try {
46
+ await hooks.onTurnComplete({
47
+ identity,
48
+ finalText
49
+ });
50
+ } catch (err) {
51
+ log("[chat-engine] onTurnComplete threw", { error: err instanceof Error ? err.message : String(err) });
52
+ }
53
+ await emit({
54
+ type: "session.run.completed",
55
+ data: { sessionId: identity.sessionId }
56
+ });
57
+ } catch (err) {
58
+ const message = err instanceof Error ? err.message : String(err);
59
+ log("[chat-engine] turn failed", { error: message });
60
+ await emit({
61
+ type: "error",
62
+ data: { message }
63
+ });
64
+ await emit({
65
+ type: "session.run.failed",
66
+ data: {
67
+ sessionId: identity.sessionId,
68
+ message
69
+ }
70
+ });
71
+ } finally {
72
+ if (hooks.traceFlush) {
73
+ const flush = hooks.traceFlush().catch((err) => log("[chat-engine] traceFlush threw", { error: err instanceof Error ? err.message : String(err) }));
74
+ if (input.waitUntil) input.waitUntil(flush);
75
+ else await flush;
76
+ }
77
+ controller.close();
78
+ }
79
+ } }),
80
+ contentType: "application/x-ndjson"
81
+ };
82
+ }
83
+ //#endregion
84
+ //#region src/durable/execution-handle.ts
85
+ /**
86
+ * Derive a stable execution id from the run identity.
87
+ * The same `(projectId, sessionId, turnIndex)` tuple yields the same id.
88
+ *
89
+ * Use the result as both `PromptOptions.executionId` and
90
+ * `PromptOptions.turnId` on the first dispatch.
91
+ * The execution id addresses the server-side execution for reconnect and
92
+ * replay; the turn id makes a repeated dispatch idempotent.
93
+ * An execution id alone does not make a repeated POST idempotent.
94
+ *
95
+ * Format is readable, not hashed: operators grepping orchestrator logs
96
+ * for `gtm-agent:thread-abc:3` find the run without translating an
97
+ * opaque id. Components are URL-encoded so delimiters inside caller ids
98
+ * cannot collapse distinct tuples. The final id is limited to the
99
+ * orchestrator replay route's 256-byte maximum. Execution ids are not a
100
+ * secrecy boundary.
101
+ *
102
+ * Wire integration:
103
+ * - Initial dispatch: pass the result as `executionId` and `turnId`.
104
+ * - Stream replay: pass it as `executionId` with `lastEventId`.
105
+ *
106
+ * @throws `TypeError` when either string id is blank.
107
+ * @throws `RangeError` when `turnIndex` is invalid or the result exceeds 256 bytes.
108
+ */
109
+ function deriveExecutionId(input) {
110
+ if (input.projectId.trim().length === 0) throw new TypeError("projectId must be a non-empty string");
111
+ if (input.sessionId.trim().length === 0) throw new TypeError("sessionId must be a non-empty string");
112
+ if (!Number.isSafeInteger(input.turnIndex) || input.turnIndex < 0) throw new RangeError("turnIndex must be a non-negative safe integer");
113
+ const executionId = [
114
+ encodeURIComponent(input.projectId),
115
+ encodeURIComponent(input.sessionId),
116
+ String(input.turnIndex)
117
+ ].join(":");
118
+ if (executionId.length > 256) throw new RangeError("derived execution id must not exceed 256 bytes");
119
+ return executionId;
120
+ }
121
+ //#endregion
122
+ export { deriveExecutionId, handleChatTurn };
123
+
124
+ //# sourceMappingURL=durable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable.js","names":[],"sources":["../src/durable/chat-engine.ts","../src/durable/execution-handle.ts"],"sourcesContent":["/**\n * `handleChatTurn` is a framework-neutral chat-turn HTTP orchestrator.\n * Owns the NDJSON `ChatStreamEvent` line protocol, the `session.run.*`\n * lifecycle vocabulary, and the persist / post-process / trace-flush\n * hook order. Returns a `ReadableStream` body the product hands to its\n * platform `Response`.\n *\n * Sandbox owns long-running execution, reconnect, and replay.\n * The producer this engine wraps already speaks that protocol; this\n * engine frames the events and orders product callbacks.\n *\n * Hooks (`ChatTurnHooks`):\n * - `produce`: build the backend event stream\n * - `persistAssistantMessage`: write the assistant turn to the product DB\n * - `onTurnComplete?`: post-process proposals, citations, and similar work\n * - `onEvent?`: send each event to another consumer\n * - `transformFinalText?`: transform text before persistence\n * - `traceFlush?`: let `waitUntil` keep the worker alive for export\n *\n * Framework neutrality: takes already-resolved values (`identity` tuple,\n * a `waitUntil`), never a `Request` or a `Context`. The product's thin\n * route adapter does auth + parse + access-control, then calls\n * `handleChatTurn(...)` and returns `result.body` as its platform `Response`.\n */\n\n/** The NDJSON line protocol every product chat client already speaks. */\nexport interface ChatStreamEvent {\n type: string\n data?: Record<string, unknown>\n}\n\n/** Identity of a chat turn. `tenantId` is the workspace id for workspace-\n * scoped products and the user id for session-scoped products. */\nexport interface ChatTurnIdentity {\n tenantId: string\n /** Thread / session id. */\n sessionId: string\n userId: string\n /** Monotonic 0-based turn index within the session. */\n turnIndex: number\n}\n\n/** The live side of a turn returned by the product's `produce` hook. */\nexport interface ChatTurnProducer<TEvent extends ChatStreamEvent = ChatStreamEvent> {\n /** The turn's event stream. Forwarded verbatim to the caller. */\n stream: AsyncGenerator<TEvent, void, unknown>\n /** The turn's final assistant text. Read once, after `stream` drains. */\n finalText(): string\n}\n\n/** Product callbacks invoked while one chat turn runs. */\nexport interface ChatTurnHooks {\n /** Build the backend stream. The engine forwards events verbatim and\n * reads `finalText()` once the stream drains. */\n produce(): ChatTurnProducer\n /** Persist the assistant message to the product's own store. Called\n * once, after drain, with the assembled (transform-applied) text. */\n persistAssistantMessage(input: { identity: ChatTurnIdentity; finalText: string }): Promise<void>\n /** Optional post-processing for proposals, citations, or credit metering.\n * Errors are logged without failing a turn that already streamed. */\n onTurnComplete?(input: { identity: ChatTurnIdentity; finalText: string }): Promise<void>\n /** Optional per-event side channel, such as a Durable Object broadcast.\n * Runs for every emitted event, including the lifecycle envelope.\n * Errors are logged without breaking the chat stream. */\n onEvent?(event: ChatStreamEvent): void | Promise<void>\n /** Optional pre-persist transform of the final text (e.g. PII\n * redaction). Affects only what is persisted; the live stream is\n * never altered. */\n transformFinalText?(text: string): string | Promise<string>\n /** Optional trace flush. Handed to `waitUntil` so the worker stays alive\n * until export completes. */\n traceFlush?(): Promise<void>\n}\n\n/** Inputs for one streamed product chat turn. */\nexport interface RunChatTurnInput {\n identity: ChatTurnIdentity\n hooks: ChatTurnHooks\n /** Worker liveness hook. When omitted, trace flush is awaited inline\n * before the stream closes. */\n waitUntil?: (p: Promise<unknown>) => void\n /** Structured logger for swallowed hook errors. Defaults to\n * `console.error` so failures surface without product wiring. */\n log?: (message: string, meta?: Record<string, unknown>) => void\n}\n\n/** HTTP response values returned for one chat turn. */\nexport interface ChatTurnResult {\n /** NDJSON body to return as the platform `Response` body. */\n body: ReadableStream<Uint8Array>\n /** Content type for the response. */\n contentType: 'application/x-ndjson'\n}\n\nconst encoder = new TextEncoder()\n\nfunction encodeLine(event: ChatStreamEvent): Uint8Array {\n return encoder.encode(`${JSON.stringify(event)}\\n`)\n}\n\nfunction defaultLog(message: string, meta?: Record<string, unknown>): void {\n if (meta) console.error(message, meta)\n else console.error(message)\n}\n\n/**\n * Run one chat turn. Returns immediately with a `ReadableStream` body;\n * execution starts while the stream is constructed. Backend\n * failures surface as `error` + `session.run.failed` events.\n */\nexport function handleChatTurn(input: RunChatTurnInput): ChatTurnResult {\n const log = input.log ?? defaultLog\n const { identity, hooks } = input\n\n const body = new ReadableStream<Uint8Array>({\n start: async (controller) => {\n const emit = async (event: ChatStreamEvent): Promise<void> => {\n controller.enqueue(encodeLine(event))\n if (hooks.onEvent) {\n try {\n await hooks.onEvent(event)\n } catch (err) {\n log('[chat-engine] onEvent hook threw', {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n }\n\n try {\n await emit({\n type: 'session.run.started',\n data: {\n sessionId: identity.sessionId,\n tenantId: identity.tenantId,\n turnIndex: identity.turnIndex,\n },\n })\n\n const producer = hooks.produce()\n for await (const event of producer.stream) {\n await emit(event)\n }\n const rawFinal = producer.finalText()\n const finalText = hooks.transformFinalText\n ? await hooks.transformFinalText(rawFinal)\n : rawFinal\n\n await hooks.persistAssistantMessage({ identity, finalText })\n if (hooks.onTurnComplete) {\n try {\n await hooks.onTurnComplete({ identity, finalText })\n } catch (err) {\n log('[chat-engine] onTurnComplete threw', {\n error: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n await emit({\n type: 'session.run.completed',\n data: { sessionId: identity.sessionId },\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n log('[chat-engine] turn failed', { error: message })\n await emit({ type: 'error', data: { message } })\n await emit({\n type: 'session.run.failed',\n data: { sessionId: identity.sessionId, message },\n })\n } finally {\n if (hooks.traceFlush) {\n const flush = hooks.traceFlush().catch((err) =>\n log('[chat-engine] traceFlush threw', {\n error: err instanceof Error ? err.message : String(err),\n }),\n )\n if (input.waitUntil) input.waitUntil(flush)\n else await flush\n }\n controller.close()\n }\n },\n })\n\n return { body, contentType: 'application/x-ndjson' }\n}\n","/**\n * Derive a stable execution id from the run identity.\n * The same `(projectId, sessionId, turnIndex)` tuple yields the same id.\n *\n * Use the result as both `PromptOptions.executionId` and\n * `PromptOptions.turnId` on the first dispatch.\n * The execution id addresses the server-side execution for reconnect and\n * replay; the turn id makes a repeated dispatch idempotent.\n * An execution id alone does not make a repeated POST idempotent.\n *\n * Format is readable, not hashed: operators grepping orchestrator logs\n * for `gtm-agent:thread-abc:3` find the run without translating an\n * opaque id. Components are URL-encoded so delimiters inside caller ids\n * cannot collapse distinct tuples. The final id is limited to the\n * orchestrator replay route's 256-byte maximum. Execution ids are not a\n * secrecy boundary.\n *\n * Wire integration:\n * - Initial dispatch: pass the result as `executionId` and `turnId`.\n * - Stream replay: pass it as `executionId` with `lastEventId`.\n *\n * @throws `TypeError` when either string id is blank.\n * @throws `RangeError` when `turnIndex` is invalid or the result exceeds 256 bytes.\n */\nexport function deriveExecutionId(input: {\n projectId: string\n sessionId: string\n turnIndex: number\n}): string {\n if (input.projectId.trim().length === 0) {\n throw new TypeError('projectId must be a non-empty string')\n }\n if (input.sessionId.trim().length === 0) {\n throw new TypeError('sessionId must be a non-empty string')\n }\n if (!Number.isSafeInteger(input.turnIndex) || input.turnIndex < 0) {\n throw new RangeError('turnIndex must be a non-negative safe integer')\n }\n\n const executionId = [\n encodeURIComponent(input.projectId),\n encodeURIComponent(input.sessionId),\n String(input.turnIndex),\n ].join(':')\n if (executionId.length > 256) {\n throw new RangeError('derived execution id must not exceed 256 bytes')\n }\n return executionId\n}\n"],"mappings":";AA8FA,MAAM,UAAU,IAAI,YAAY;AAEhC,SAAS,WAAW,OAAoC;CACtD,OAAO,QAAQ,OAAO,GAAG,KAAK,UAAU,KAAK,EAAE,GAAG;AACpD;AAEA,SAAS,WAAW,SAAiB,MAAsC;CACzE,IAAI,MAAM,QAAQ,MAAM,SAAS,IAAI;MAChC,QAAQ,MAAM,OAAO;AAC5B;;;;;;AAOA,SAAgB,eAAe,OAAyC;CACtE,MAAM,MAAM,MAAM,OAAO;CACzB,MAAM,EAAE,UAAU,UAAU;CA0E5B,OAAO;EAAE,MAAA,IAxEQ,eAA2B,EAC1C,OAAO,OAAO,eAAe;GAC3B,MAAM,OAAO,OAAO,UAA0C;IAC5D,WAAW,QAAQ,WAAW,KAAK,CAAC;IACpC,IAAI,MAAM,SACR,IAAI;KACF,MAAM,MAAM,QAAQ,KAAK;IAC3B,SAAS,KAAK;KACZ,IAAI,oCAAoC,EACtC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EACxD,CAAC;IACH;GAEJ;GAEA,IAAI;IACF,MAAM,KAAK;KACT,MAAM;KACN,MAAM;MACJ,WAAW,SAAS;MACpB,UAAU,SAAS;MACnB,WAAW,SAAS;KACtB;IACF,CAAC;IAED,MAAM,WAAW,MAAM,QAAQ;IAC/B,WAAW,MAAM,SAAS,SAAS,QACjC,MAAM,KAAK,KAAK;IAElB,MAAM,WAAW,SAAS,UAAU;IACpC,MAAM,YAAY,MAAM,qBACpB,MAAM,MAAM,mBAAmB,QAAQ,IACvC;IAEJ,MAAM,MAAM,wBAAwB;KAAE;KAAU;IAAU,CAAC;IAC3D,IAAI,MAAM,gBACR,IAAI;KACF,MAAM,MAAM,eAAe;MAAE;MAAU;KAAU,CAAC;IACpD,SAAS,KAAK;KACZ,IAAI,sCAAsC,EACxC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EACxD,CAAC;IACH;IAGF,MAAM,KAAK;KACT,MAAM;KACN,MAAM,EAAE,WAAW,SAAS,UAAU;IACxC,CAAC;GACH,SAAS,KAAK;IACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;IAC/D,IAAI,6BAA6B,EAAE,OAAO,QAAQ,CAAC;IACnD,MAAM,KAAK;KAAE,MAAM;KAAS,MAAM,EAAE,QAAQ;IAAE,CAAC;IAC/C,MAAM,KAAK;KACT,MAAM;KACN,MAAM;MAAE,WAAW,SAAS;MAAW;KAAQ;IACjD,CAAC;GACH,UAAU;IACR,IAAI,MAAM,YAAY;KACpB,MAAM,QAAQ,MAAM,WAAW,CAAC,CAAC,OAAO,QACtC,IAAI,kCAAkC,EACpC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EACxD,CAAC,CACH;KACA,IAAI,MAAM,WAAW,MAAM,UAAU,KAAK;UACrC,MAAM;IACb;IACA,WAAW,MAAM;GACnB;EACF,EACF,CAEY;EAAG,aAAa;CAAuB;AACrD;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnKA,SAAgB,kBAAkB,OAIvB;CACT,IAAI,MAAM,UAAU,KAAK,CAAC,CAAC,WAAW,GACpC,MAAM,IAAI,UAAU,sCAAsC;CAE5D,IAAI,MAAM,UAAU,KAAK,CAAC,CAAC,WAAW,GACpC,MAAM,IAAI,UAAU,sCAAsC;CAE5D,IAAI,CAAC,OAAO,cAAc,MAAM,SAAS,KAAK,MAAM,YAAY,GAC9D,MAAM,IAAI,WAAW,+CAA+C;CAGtE,MAAM,cAAc;EAClB,mBAAmB,MAAM,SAAS;EAClC,mBAAmB,MAAM,SAAS;EAClC,OAAO,MAAM,SAAS;CACxB,CAAC,CAAC,KAAK,GAAG;CACV,IAAI,YAAY,SAAS,KACvB,MAAM,IAAI,WAAW,gDAAgD;CAEvE,OAAO;AACT"}
@@ -496,7 +496,44 @@ interface SpawnOpts {
496
496
  readonly restart?: Restart;
497
497
  /** Teardown grace handed to the executor when this node is reaped. */
498
498
  readonly shutdown?: number | 'brutalKill' | 'infinity';
499
+ /**
500
+ * Semantic identity of this assignment ACROSS process lifetimes. A keyed spawn is
501
+ * idempotent per key: once a child spawned under a key settles `done` — in this process or in a
502
+ * journaled prior one — spawning the same key returns that committed result (`prior.state:
503
+ * 'completed'`) instead of paying for the work again. A key whose prior attempt settled `down`
504
+ * or was journaled as started-but-never-settled spawns FRESH but says so explicitly
505
+ * (`prior.state: 'retried' | 'lost'`), and a key that is currently LIVE is refused
506
+ * (`'duplicate-key'`) — the same assignment can never run twice concurrently. Unkeyed spawns
507
+ * (the default) are position-identified and always run.
508
+ */
509
+ readonly key?: string;
499
510
  }
511
+ /** Fail-closed spawn rejections: an exhausted pool, an exceeded recursion ceiling, or a `key`
512
+ * that is still LIVE in this scope (the same assignment may not run twice concurrently). */
513
+ type SpawnRejection = 'budget-exhausted' | 'depth-exceeded' | 'duplicate-key';
514
+ /**
515
+ * What a KEYED spawn resolved to when the key had a prior attempt. Absent on a fresh key (and on
516
+ * every unkeyed spawn). `'completed'` is the exactly-once path: NOTHING was spawned — the handle
517
+ * references the prior settled node and `settled` is the committed result. `'retried'` /
518
+ * `'lost'` DID spawn fresh: the prior attempt settled `down` (retried) or was journaled as
519
+ * started but never settled — the process died with it in flight and the built-in executors
520
+ * cannot re-attach to a dead process's work, so the result is explicitly in doubt (lost), never
521
+ * silently duplicated. An executor that CAN re-attach to a still-running external execution (a
522
+ * live sandbox box) extends this union with an adoption state; none of the built-ins can today.
523
+ */
524
+ type SpawnPrior<Out = unknown> = {
525
+ readonly state: 'completed';
526
+ readonly settled: Settled<Out> & {
527
+ kind: 'done';
528
+ };
529
+ } | {
530
+ readonly state: 'retried';
531
+ readonly priorId: NodeId;
532
+ readonly reason: string;
533
+ } | {
534
+ readonly state: 'lost';
535
+ readonly priorId: NodeId;
536
+ };
500
537
  /**
501
538
  * A live child handle. `abort()` is defined over the ACQUIRE lifecycle: it chains into
502
539
  * the `acquireSandbox` signal and reaps a find-by-name orphan box, so a node aborted
@@ -543,14 +580,17 @@ interface Scope<Out> {
543
580
  /**
544
581
  * Spawn a child. Reserves `opts.budget` from the conserved pool atomically; refunds the
545
582
  * unspent remainder on settle. Returns a typed outcome — fail-closed on an exhausted
546
- * pool or an exceeded depth ceiling (the caller inspects `ok` before `handle`).
583
+ * pool, an exceeded depth ceiling, or a still-live duplicate `key` (the caller inspects
584
+ * `ok` before `handle`). A KEYED spawn whose key already settled `done` spends nothing:
585
+ * it returns the committed result on `prior` instead of re-running (see `SpawnOpts.key`).
547
586
  */
548
587
  spawn<C extends Out>(agent: Agent<unknown, C>, task: unknown, opts: SpawnOpts): {
549
588
  ok: true;
550
589
  handle: Handle<C>;
590
+ prior?: SpawnPrior<C>;
551
591
  } | {
552
592
  ok: false;
553
- reason: 'budget-exhausted' | 'depth-exceeded';
593
+ reason: SpawnRejection;
554
594
  };
555
595
  /** ray.wait n=1 over this scope's in-memory live set; resolves as each child settles;
556
596
  * `null` when the live set is empty. */
@@ -668,6 +708,32 @@ interface ResumedWork<Out> {
668
708
  * resumed run that was not waiting.
669
709
  */
670
710
  readonly waits: ReadonlyArray<PendingWait>;
711
+ /**
712
+ * Keyed assignments from the prior journal: `SpawnOpts.key` → what the journal proves about it.
713
+ * `completed`/`down` carry the rehydrated settlement; `in-doubt` means the spawn was journaled
714
+ * but no settlement ever landed — the process died with it in flight. `Scope.spawn` consults
715
+ * this so a keyed re-spawn resolves instead of duplicating (see `SpawnOpts.key`). Empty when no
716
+ * prior spawn carried a key.
717
+ */
718
+ readonly keys: ReadonlyMap<string, ResumedKeyState<Out>>;
719
+ /**
720
+ * The conserved spend the prior process(es) already committed for this run, summed off the same
721
+ * journal replay reads: every `settled` child's reconciled spend (`childWork`) plus every
722
+ * `metered` driver-inference record (`driverInference`). What a resume-aware driver reports as
723
+ * "already paid" — the run's final `spentTotal` includes it because the journal spans processes.
724
+ */
725
+ readonly priorSpend: {
726
+ readonly childWork: Spend;
727
+ readonly driverInference: Spend;
728
+ };
729
+ }
730
+ /** What the journal proves about one keyed assignment at resume time. */
731
+ interface ResumedKeyState<Out = unknown> {
732
+ readonly id: NodeId;
733
+ readonly label: string;
734
+ readonly state: 'completed' | 'down' | 'in-doubt';
735
+ /** The rehydrated settlement; absent exactly when `state` is `'in-doubt'`. */
736
+ readonly settled?: Settled<Out>;
671
737
  }
672
738
  interface NodeSnapshot {
673
739
  readonly id: NodeId;
@@ -699,6 +765,9 @@ type SpawnEvent = {
699
765
  id: NodeId;
700
766
  parent?: NodeId;
701
767
  label: string;
768
+ /** The semantic spawn key (`SpawnOpts.key`), when the spawn carried one — what a resumed
769
+ * run matches to resolve the same assignment to its committed result. */
770
+ key?: string;
702
771
  budget: Budget;
703
772
  runtime: Runtime;
704
773
  seq: number;
@@ -960,5 +1029,5 @@ interface ProviderExecutorOptions {
960
1029
  * @experimental */
961
1030
  declare function providerAsExecutor(provider: AgentEnvironmentProvider, options?: ProviderExecutorOptions): ExecutorFactory<unknown>;
962
1031
  //#endregion
963
- export { RootSignal as $, providerAsSandboxClient as A, createPushTraceSource as At, Executor as B, readWorkerProgress as Bt, ProviderAsSandboxClientOptions as C, timerAt as Ct, WorkspaceRequest as D, SessionTraceBox as Dt, SandboxClientProviderOptions as E, SessionMessageLike as Et, createTangleSandboxExactProcessProvider as F, DEFAULT_STALL_AFTER_MS as Ft, Handle as G, ExecutorFactory as H, Agent as I, ExecutorProgress as It, NodeStatus as J, NodeId as K, AgentSpec as L, ScopeProgressInput as Lt, sandboxClientAsProvider as M, sandboxSessionTraceSource as Mt, CreateTangleSandboxExactProcessProviderOptions as N, ActivityLog as Nt, createAgentEnvironmentProviderRegistry as O, ToolStepInput as Ot, SandboxControlClient as P, ActivityNote as Pt, RootHandle as Q, Budget as R, WorkerProgress as Rt, PlacementInfo as S, pollFor as St, ResourceRequest as T, waitUntil as Tt, ExecutorRegistry as U, ExecutorContext as V, ExecutorResult as W, ResultBlobStore as X, Restart as Y, ResumedWork as Z, CheckpointRequest as _, WaitProbeRegistry as _t, AgentEnvironmentProviderRef as a, SpawnOpts as at, ExecResult as b, createWaitProbes as bt, AgentEnvironmentStatus as c, Supervisor as ct, AgentSession as d, UsageEvent as dt, Runtime as et, AgentSessionRef as f, WaitOpts as ft, CheckpointRef as g, WaitProbe as gt, AgentTurnResult as h, WaitOutcome as ht, AgentEnvironmentProvider$1 as i, SpawnJournal as it, resolveAgentEnvironmentProvider as j, decodeToolPart as jt, providerAsExecutor as k, TraceSource as kt, AgentEnvironmentSummary as l, SupervisorOpts as lt, AgentTurnInput$1 as m, PendingWait as mt, AgentEnvironmentCapabilities$1 as n, Settled as nt, AgentEnvironmentProviderRegistry as o, Spend as ot, AgentSessionStatus as p, WidenGate as pt, NodeSnapshot as q, AgentEnvironmentEvent as r, SpawnEvent as rt, AgentEnvironmentQuery as s, SupervisedResult as st, AgentEnvironment as t, Scope as tt, AgentProfileRef$1 as u, TreeView as ut, CreateAgentEnvironmentInput$1 as v, WaitRejection as vt, ProviderExecutorOptions as w, validateWaitSpec as wt, ForkRequest as x, isWaitOutcome as xt, ExecRequest as y, WaitSpec as yt, DefaultVerdict as z, createActivityLog as zt };
964
- //# sourceMappingURL=environment-provider-IUGU3epE.d.ts.map
1032
+ export { RootHandle as $, providerAsSandboxClient as A, SessionTraceBox as At, Executor as B, ScopeProgressInput as Bt, ProviderAsSandboxClientOptions as C, createWaitProbes as Ct, WorkspaceRequest as D, validateWaitSpec as Dt, SandboxClientProviderOptions as E, timerAt as Et, createTangleSandboxExactProcessProvider as F, sandboxSessionTraceSource as Ft, Handle as G, ExecutorFactory as H, createActivityLog as Ht, Agent as I, ActivityLog as It, NodeStatus as J, NodeId as K, AgentSpec as L, ActivityNote as Lt, sandboxClientAsProvider as M, TraceSource as Mt, CreateTangleSandboxExactProcessProviderOptions as N, createPushTraceSource as Nt, createAgentEnvironmentProviderRegistry as O, waitUntil as Ot, SandboxControlClient as P, decodeToolPart as Pt, ResumedWork as Q, Budget as R, DEFAULT_STALL_AFTER_MS as Rt, PlacementInfo as S, WaitSpec as St, ResourceRequest as T, pollFor as Tt, ExecutorRegistry as U, readWorkerProgress as Ut, ExecutorContext as V, WorkerProgress as Vt, ExecutorResult as W, ResultBlobStore as X, Restart as Y, ResumedKeyState as Z, CheckpointRequest as _, PendingWait as _t, AgentEnvironmentProviderRef as a, SpawnJournal as at, ExecResult as b, WaitProbeRegistry as bt, AgentEnvironmentStatus as c, SpawnRejection as ct, AgentSession as d, Supervisor as dt, RootSignal as et, AgentSessionRef as f, SupervisorOpts as ft, CheckpointRef as g, WidenGate as gt, AgentTurnResult as h, WaitOpts as ht, AgentEnvironmentProvider$1 as i, SpawnEvent as it, resolveAgentEnvironmentProvider as j, ToolStepInput as jt, providerAsExecutor as k, SessionMessageLike as kt, AgentEnvironmentSummary as l, Spend as lt, AgentTurnInput$1 as m, UsageEvent as mt, AgentEnvironmentCapabilities$1 as n, Scope as nt, AgentEnvironmentProviderRegistry as o, SpawnOpts as ot, AgentSessionStatus as p, TreeView as pt, NodeSnapshot as q, AgentEnvironmentEvent as r, Settled as rt, AgentEnvironmentQuery as s, SpawnPrior as st, AgentEnvironment as t, Runtime as tt, AgentProfileRef$1 as u, SupervisedResult as ut, CreateAgentEnvironmentInput$1 as v, WaitOutcome as vt, ProviderExecutorOptions as w, isWaitOutcome as wt, ForkRequest as x, WaitRejection as xt, ExecRequest as y, WaitProbe as yt, DefaultVerdict as z, ExecutorProgress as zt };
1033
+ //# sourceMappingURL=environment-provider-D0NXc4Qz.d.ts.map
@@ -1,2 +1,2 @@
1
- import { A as providerAsSandboxClient, C as ProviderAsSandboxClientOptions, D as WorkspaceRequest, E as SandboxClientProviderOptions, F as createTangleSandboxExactProcessProvider, M as sandboxClientAsProvider, N as CreateTangleSandboxExactProcessProviderOptions, O as createAgentEnvironmentProviderRegistry, S as PlacementInfo, T as ResourceRequest, _ as CheckpointRequest, a as AgentEnvironmentProviderRef, b as ExecResult, c as AgentEnvironmentStatus, d as AgentSession, f as AgentSessionRef, g as CheckpointRef, h as AgentTurnResult, i as AgentEnvironmentProvider, j as resolveAgentEnvironmentProvider, k as providerAsExecutor, l as AgentEnvironmentSummary, m as AgentTurnInput, n as AgentEnvironmentCapabilities, o as AgentEnvironmentProviderRegistry, p as AgentSessionStatus, r as AgentEnvironmentEvent, s as AgentEnvironmentQuery, t as AgentEnvironment, u as AgentProfileRef, v as CreateAgentEnvironmentInput, w as ProviderExecutorOptions, x as ForkRequest, y as ExecRequest } from "./environment-provider-IUGU3epE.js";
1
+ import { A as providerAsSandboxClient, C as ProviderAsSandboxClientOptions, D as WorkspaceRequest, E as SandboxClientProviderOptions, F as createTangleSandboxExactProcessProvider, M as sandboxClientAsProvider, N as CreateTangleSandboxExactProcessProviderOptions, O as createAgentEnvironmentProviderRegistry, S as PlacementInfo, T as ResourceRequest, _ as CheckpointRequest, a as AgentEnvironmentProviderRef, b as ExecResult, c as AgentEnvironmentStatus, d as AgentSession, f as AgentSessionRef, g as CheckpointRef, h as AgentTurnResult, i as AgentEnvironmentProvider, j as resolveAgentEnvironmentProvider, k as providerAsExecutor, l as AgentEnvironmentSummary, m as AgentTurnInput, n as AgentEnvironmentCapabilities, o as AgentEnvironmentProviderRegistry, p as AgentSessionStatus, r as AgentEnvironmentEvent, s as AgentEnvironmentQuery, t as AgentEnvironment, u as AgentProfileRef, v as CreateAgentEnvironmentInput, w as ProviderExecutorOptions, x as ForkRequest, y as ExecRequest } from "./environment-provider-D0NXc4Qz.js";
2
2
  export { type AgentEnvironment, type AgentEnvironmentCapabilities, type AgentEnvironmentEvent, type AgentEnvironmentProvider, AgentEnvironmentProviderRef, AgentEnvironmentProviderRegistry, type AgentEnvironmentQuery, type AgentEnvironmentStatus, type AgentEnvironmentSummary, type AgentProfileRef, type AgentSession, type AgentSessionRef, type AgentSessionStatus, type AgentTurnInput, type AgentTurnResult, type CheckpointRef, type CheckpointRequest, type CreateAgentEnvironmentInput, type CreateTangleSandboxExactProcessProviderOptions, type ExecRequest, type ExecResult, type ForkRequest, type PlacementInfo, ProviderAsSandboxClientOptions, ProviderExecutorOptions, type ResourceRequest, SandboxClientProviderOptions, type WorkspaceRequest, createAgentEnvironmentProviderRegistry, createTangleSandboxExactProcessProvider, providerAsExecutor, providerAsSandboxClient, resolveAgentEnvironmentProvider, sandboxClientAsProvider };