zeitlich 0.2.21 → 0.2.23

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 (129) hide show
  1. package/README.md +303 -105
  2. package/dist/adapters/sandbox/daytona/index.cjs +7 -1
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  4. package/dist/adapters/sandbox/daytona/index.d.cts +3 -1
  5. package/dist/adapters/sandbox/daytona/index.d.ts +3 -1
  6. package/dist/adapters/sandbox/daytona/index.js +7 -1
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  8. package/dist/adapters/sandbox/daytona/workflow.cjs +33 -0
  9. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -0
  10. package/dist/adapters/sandbox/daytona/workflow.d.cts +27 -0
  11. package/dist/adapters/sandbox/daytona/workflow.d.ts +27 -0
  12. package/dist/adapters/sandbox/daytona/workflow.js +31 -0
  13. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -0
  14. package/dist/adapters/sandbox/inmemory/index.cjs +18 -1
  15. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  16. package/dist/adapters/sandbox/inmemory/index.d.cts +4 -2
  17. package/dist/adapters/sandbox/inmemory/index.d.ts +4 -2
  18. package/dist/adapters/sandbox/inmemory/index.js +18 -1
  19. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  20. package/dist/adapters/sandbox/inmemory/workflow.cjs +33 -0
  21. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -0
  22. package/dist/adapters/sandbox/inmemory/workflow.d.cts +25 -0
  23. package/dist/adapters/sandbox/inmemory/workflow.d.ts +25 -0
  24. package/dist/adapters/sandbox/inmemory/workflow.js +31 -0
  25. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -0
  26. package/dist/adapters/sandbox/virtual/index.cjs +36 -9
  27. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/virtual/index.d.cts +8 -5
  29. package/dist/adapters/sandbox/virtual/index.d.ts +8 -5
  30. package/dist/adapters/sandbox/virtual/index.js +36 -9
  31. package/dist/adapters/sandbox/virtual/index.js.map +1 -1
  32. package/dist/adapters/sandbox/virtual/workflow.cjs +33 -0
  33. package/dist/adapters/sandbox/virtual/workflow.cjs.map +1 -0
  34. package/dist/adapters/sandbox/virtual/workflow.d.cts +27 -0
  35. package/dist/adapters/sandbox/virtual/workflow.d.ts +27 -0
  36. package/dist/adapters/sandbox/virtual/workflow.js +31 -0
  37. package/dist/adapters/sandbox/virtual/workflow.js.map +1 -0
  38. package/dist/adapters/thread/google-genai/index.cjs +9 -1
  39. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  40. package/dist/adapters/thread/google-genai/index.d.cts +31 -19
  41. package/dist/adapters/thread/google-genai/index.d.ts +31 -19
  42. package/dist/adapters/thread/google-genai/index.js +9 -1
  43. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  44. package/dist/adapters/thread/google-genai/workflow.cjs +33 -0
  45. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -0
  46. package/dist/adapters/thread/google-genai/workflow.d.cts +32 -0
  47. package/dist/adapters/thread/google-genai/workflow.d.ts +32 -0
  48. package/dist/adapters/thread/google-genai/workflow.js +31 -0
  49. package/dist/adapters/thread/google-genai/workflow.js.map +1 -0
  50. package/dist/adapters/thread/langchain/index.cjs +9 -1
  51. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  52. package/dist/adapters/thread/langchain/index.d.cts +27 -16
  53. package/dist/adapters/thread/langchain/index.d.ts +27 -16
  54. package/dist/adapters/thread/langchain/index.js +9 -1
  55. package/dist/adapters/thread/langchain/index.js.map +1 -1
  56. package/dist/adapters/thread/langchain/workflow.cjs +33 -0
  57. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -0
  58. package/dist/adapters/thread/langchain/workflow.d.cts +32 -0
  59. package/dist/adapters/thread/langchain/workflow.d.ts +32 -0
  60. package/dist/adapters/thread/langchain/workflow.js +31 -0
  61. package/dist/adapters/thread/langchain/workflow.js.map +1 -0
  62. package/dist/index.cjs +282 -90
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +38 -16
  65. package/dist/index.d.ts +38 -16
  66. package/dist/index.js +281 -87
  67. package/dist/index.js.map +1 -1
  68. package/dist/queries-DModcWRy.d.cts +44 -0
  69. package/dist/queries-byD0jr1Y.d.ts +44 -0
  70. package/dist/{types-BkAYmc96.d.ts → types-B50pBPEV.d.ts} +190 -38
  71. package/dist/{types-YbL7JpEA.d.cts → types-Bll19FZJ.d.cts} +7 -0
  72. package/dist/{types-YbL7JpEA.d.ts → types-Bll19FZJ.d.ts} +7 -0
  73. package/dist/{queries-6Avfh74U.d.ts → types-BuXdFhaZ.d.cts} +7 -48
  74. package/dist/{types-BMRzfELQ.d.cts → types-ChAMwU3q.d.cts} +17 -1
  75. package/dist/{types-BMRzfELQ.d.ts → types-ChAMwU3q.d.ts} +17 -1
  76. package/dist/{types-CES_30qx.d.cts → types-DQW8l7pY.d.cts} +190 -38
  77. package/dist/{queries-CHa2iv_I.d.cts → types-GZ76HZSj.d.ts} +7 -48
  78. package/dist/workflow.cjs +244 -86
  79. package/dist/workflow.cjs.map +1 -1
  80. package/dist/workflow.d.cts +54 -65
  81. package/dist/workflow.d.ts +54 -65
  82. package/dist/workflow.js +243 -83
  83. package/dist/workflow.js.map +1 -1
  84. package/package.json +54 -2
  85. package/src/adapters/sandbox/daytona/filesystem.ts +1 -1
  86. package/src/adapters/sandbox/daytona/index.ts +8 -0
  87. package/src/adapters/sandbox/daytona/proxy.ts +56 -0
  88. package/src/adapters/sandbox/e2b/filesystem.ts +147 -0
  89. package/src/adapters/sandbox/e2b/index.ts +164 -0
  90. package/src/adapters/sandbox/e2b/types.ts +23 -0
  91. package/src/adapters/sandbox/inmemory/index.ts +27 -3
  92. package/src/adapters/sandbox/inmemory/proxy.ts +53 -0
  93. package/src/adapters/sandbox/virtual/filesystem.ts +41 -17
  94. package/src/adapters/sandbox/virtual/provider.ts +9 -1
  95. package/src/adapters/sandbox/virtual/proxy.ts +53 -0
  96. package/src/adapters/sandbox/virtual/types.ts +9 -4
  97. package/src/adapters/thread/google-genai/activities.ts +51 -17
  98. package/src/adapters/thread/google-genai/index.ts +1 -0
  99. package/src/adapters/thread/google-genai/proxy.ts +61 -0
  100. package/src/adapters/thread/langchain/activities.ts +47 -14
  101. package/src/adapters/thread/langchain/index.ts +1 -0
  102. package/src/adapters/thread/langchain/proxy.ts +61 -0
  103. package/src/lib/lifecycle.ts +57 -0
  104. package/src/lib/sandbox/manager.ts +52 -6
  105. package/src/lib/sandbox/sandbox.test.ts +12 -11
  106. package/src/lib/sandbox/types.ts +31 -4
  107. package/src/lib/session/index.ts +4 -5
  108. package/src/lib/session/session-edge-cases.integration.test.ts +491 -66
  109. package/src/lib/session/session.integration.test.ts +92 -80
  110. package/src/lib/session/session.ts +108 -96
  111. package/src/lib/session/types.ts +87 -17
  112. package/src/lib/subagent/define.ts +6 -5
  113. package/src/lib/subagent/handler.ts +148 -16
  114. package/src/lib/subagent/index.ts +4 -0
  115. package/src/lib/subagent/register.ts +10 -3
  116. package/src/lib/subagent/signals.ts +8 -0
  117. package/src/lib/subagent/subagent.integration.test.ts +893 -128
  118. package/src/lib/subagent/tool.ts +2 -2
  119. package/src/lib/subagent/types.ts +84 -21
  120. package/src/lib/subagent/workflow.ts +83 -12
  121. package/src/lib/tool-router/router-edge-cases.integration.test.ts +4 -1
  122. package/src/lib/tool-router/router.integration.test.ts +141 -5
  123. package/src/lib/tool-router/router.ts +13 -3
  124. package/src/lib/tool-router/types.ts +7 -0
  125. package/src/lib/workflow.test.ts +104 -27
  126. package/src/lib/workflow.ts +37 -19
  127. package/src/tools/bash/bash.test.ts +16 -7
  128. package/src/workflow.ts +11 -14
  129. package/tsup.config.ts +6 -0
@@ -0,0 +1,31 @@
1
+ import { workflowInfo, proxyActivities } from '@temporalio/workflow';
2
+
3
+ // src/adapters/thread/langchain/proxy.ts
4
+ var ADAPTER_PREFIX = "langChain";
5
+ function proxyLangChainThreadOps(scope, options) {
6
+ const resolvedScope = scope ?? workflowInfo().workflowType;
7
+ const acts = proxyActivities(
8
+ options ?? {
9
+ startToCloseTimeout: "10s",
10
+ retry: {
11
+ maximumAttempts: 6,
12
+ initialInterval: "5s",
13
+ maximumInterval: "15m",
14
+ backoffCoefficient: 4
15
+ }
16
+ }
17
+ );
18
+ const prefix = `${ADAPTER_PREFIX}${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;
19
+ const p = (key) => `${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;
20
+ return {
21
+ initializeThread: acts[p("initializeThread")],
22
+ appendHumanMessage: acts[p("appendHumanMessage")],
23
+ appendToolResult: acts[p("appendToolResult")],
24
+ appendSystemMessage: acts[p("appendSystemMessage")],
25
+ forkThread: acts[p("forkThread")]
26
+ };
27
+ }
28
+
29
+ export { proxyLangChainThreadOps };
30
+ //# sourceMappingURL=workflow.js.map
31
+ //# sourceMappingURL=workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/adapters/thread/langchain/proxy.ts"],"names":[],"mappings":";;;AA2BA,IAAM,cAAA,GAAiB,WAAA;AAEhB,SAAS,uBAAA,CACd,OACA,OAAA,EACiC;AACjC,EAAA,MAAM,aAAA,GAAgB,KAAA,IAAS,YAAA,EAAa,CAAE,YAAA;AAG9C,EAAA,MAAM,IAAA,GAAO,eAAA;AAAA,IACX,OAAA,IAAW;AAAA,MACT,mBAAA,EAAqB,KAAA;AAAA,MACrB,KAAA,EAAO;AAAA,QACL,eAAA,EAAiB,CAAA;AAAA,QACjB,eAAA,EAAiB,IAAA;AAAA,QACjB,eAAA,EAAiB,KAAA;AAAA,QACjB,kBAAA,EAAoB;AAAA;AACtB;AACF,GACF;AAEA,EAAA,MAAM,MAAA,GACJ,CAAA,EAAG,cAAc,CAAA,EAAG,cAAc,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,EAAa,CAAA,EAAG,aAAA,CAAc,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACpF,EAAA,MAAM,IAAI,CAAC,GAAA,KACT,CAAA,EAAG,MAAM,GAAG,GAAA,CAAI,MAAA,CAAO,CAAC,CAAA,CAAE,aAAa,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAExD,EAAA,OAAO;AAAA,IACL,gBAAA,EAAkB,IAAA,CAAK,CAAA,CAAE,kBAAkB,CAAC,CAAA;AAAA,IAC5C,kBAAA,EAAoB,IAAA,CAAK,CAAA,CAAE,oBAAoB,CAAC,CAAA;AAAA,IAChD,gBAAA,EAAkB,IAAA,CAAK,CAAA,CAAE,kBAAkB,CAAC,CAAA;AAAA,IAC5C,mBAAA,EAAqB,IAAA,CAAK,CAAA,CAAE,qBAAqB,CAAC,CAAA;AAAA,IAClD,UAAA,EAAY,IAAA,CAAK,CAAA,CAAE,YAAY,CAAC;AAAA,GAClC;AACF","file":"workflow.js","sourcesContent":["/**\n * Workflow-safe proxy for LangChain thread operations.\n *\n * Import this from `zeitlich/adapters/thread/langchain/workflow`\n * in your Temporal workflow files.\n *\n * By default the scope is derived from `workflowInfo().workflowType`,\n * so activities are automatically namespaced per workflow.\n *\n * @example\n * ```typescript\n * import { proxyLangChainThreadOps } from 'zeitlich/adapters/thread/langchain/workflow';\n *\n * // Auto-scoped to the current workflow name\n * const threadOps = proxyLangChainThreadOps();\n *\n * // Explicit scope override\n * const threadOps = proxyLangChainThreadOps(\"customScope\");\n * ```\n */\nimport {\n proxyActivities,\n workflowInfo,\n type ActivityInterfaceFor,\n} from \"@temporalio/workflow\";\nimport type { ThreadOps } from \"../../../lib/session/types\";\n\nconst ADAPTER_PREFIX = \"langChain\";\n\nexport function proxyLangChainThreadOps(\n scope?: string,\n options?: Parameters<typeof proxyActivities>[0]\n): ActivityInterfaceFor<ThreadOps> {\n const resolvedScope = scope ?? workflowInfo().workflowType;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const acts = proxyActivities<Record<string, (...args: any[]) => any>>(\n options ?? {\n startToCloseTimeout: \"10s\",\n retry: {\n maximumAttempts: 6,\n initialInterval: \"5s\",\n maximumInterval: \"15m\",\n backoffCoefficient: 4,\n },\n }\n );\n\n const prefix =\n `${ADAPTER_PREFIX}${resolvedScope.charAt(0).toUpperCase()}${resolvedScope.slice(1)}`;\n const p = (key: string): string =>\n `${prefix}${key.charAt(0).toUpperCase()}${key.slice(1)}`;\n\n return {\n initializeThread: acts[p(\"initializeThread\")],\n appendHumanMessage: acts[p(\"appendHumanMessage\")],\n appendToolResult: acts[p(\"appendToolResult\")],\n appendSystemMessage: acts[p(\"appendSystemMessage\")],\n forkThread: acts[p(\"forkThread\")],\n } as ActivityInterfaceFor<ThreadOps>;\n}\n"]}
package/dist/index.cjs CHANGED
@@ -88,7 +88,12 @@ function createToolRouter(options) {
88
88
  result: { error: errorStr, suppressed: true }
89
89
  };
90
90
  }
91
- throw workflow.ApplicationFailure.fromError(error, { nonRetryable: true });
91
+ return {
92
+ content: JSON.stringify({
93
+ error: "The tool encountered an error. Please try again or use a different approach."
94
+ }),
95
+ result: { error: errorStr, suppressed: true }
96
+ };
92
97
  }
93
98
  async function runPostHooks(toolCall, tool, toolResult, effectiveArgs, turn, durationMs) {
94
99
  if (tool?.hooks?.onPostToolUse) {
@@ -97,7 +102,8 @@ function createToolRouter(options) {
97
102
  result: toolResult.data,
98
103
  threadId: options.threadId,
99
104
  turn,
100
- durationMs
105
+ durationMs,
106
+ ...toolResult.metadata && { metadata: toolResult.metadata }
101
107
  });
102
108
  }
103
109
  if (options.hooks?.onPostToolUse) {
@@ -130,6 +136,7 @@ function createToolRouter(options) {
130
136
  let result;
131
137
  let content;
132
138
  let resultAppended = false;
139
+ let metadata;
133
140
  try {
134
141
  if (tool) {
135
142
  const routerContext = {
@@ -145,6 +152,7 @@ function createToolRouter(options) {
145
152
  result = response.data;
146
153
  content = response.toolResponse;
147
154
  resultAppended = response.resultAppended === true;
155
+ metadata = response.metadata;
148
156
  } else {
149
157
  result = { error: `Unknown tool: ${toolCall.name}` };
150
158
  content = JSON.stringify(result, null, 2);
@@ -177,7 +185,8 @@ function createToolRouter(options) {
177
185
  const toolResult = {
178
186
  toolCallId: toolCall.id,
179
187
  name: toolCall.name,
180
- data: result
188
+ data: result,
189
+ ...metadata && { metadata }
181
190
  };
182
191
  await runPostHooks(
183
192
  toolCall,
@@ -281,7 +290,8 @@ function createToolRouter(options) {
281
290
  return {
282
291
  toolCallId: toolCall.id,
283
292
  name: toolCall.name,
284
- data: response.data
293
+ data: response.data,
294
+ ...response.metadata && { metadata: response.metadata }
285
295
  };
286
296
  };
287
297
  if (options.parallel) {
@@ -325,7 +335,7 @@ function getShortId(length = 12) {
325
335
  var SUBAGENT_TOOL_NAME = "Subagent";
326
336
  function buildSubagentDescription(subagents) {
327
337
  const subagentList = subagents.map((s) => {
328
- const continuation = s.allowThreadContinuation ? "\n*(Supports thread continuation \u2014 pass a threadId to resume a previous conversation)*" : "";
338
+ const continuation = s.thread && s.thread !== "new" ? "\n*(Supports thread continuation \u2014 pass a threadId to resume a previous conversation)*" : "";
329
339
  return `## ${s.agentName}
330
340
  ${s.description}${continuation}`;
331
341
  }).join("\n\n");
@@ -341,7 +351,7 @@ function createSubagentTool(subagents) {
341
351
  }
342
352
  const names = subagents.map((s) => s.agentName);
343
353
  const hasThreadContinuation = subagents.some(
344
- (s) => s.allowThreadContinuation
354
+ (s) => s.thread && s.thread !== "new"
345
355
  );
346
356
  const baseFields = {
347
357
  subagent: z14__default.default.enum(names).describe("The type of subagent to launch"),
@@ -360,9 +370,25 @@ function createSubagentTool(subagents) {
360
370
  schema
361
371
  };
362
372
  }
373
+ var childResultSignal = workflow.defineSignal("childResult");
374
+ var destroySandboxSignal = workflow.defineSignal("destroySandbox");
375
+
376
+ // src/lib/subagent/handler.ts
377
+ function resolveSandboxConfig(config) {
378
+ if (!config || config === "none") return { source: "none" };
379
+ if (config === "inherit") return { source: "inherit" };
380
+ if (config === "own") return { source: "own" };
381
+ return { source: "own", shutdown: config.shutdown };
382
+ }
363
383
  function createSubagentHandler(subagents) {
364
384
  const { taskQueue: parentTaskQueue } = workflow.workflowInfo();
365
- return async (args, context) => {
385
+ const childResults = /* @__PURE__ */ new Map();
386
+ const pendingDestroys = /* @__PURE__ */ new Map();
387
+ const threadSandboxes = /* @__PURE__ */ new Map();
388
+ workflow.setHandler(childResultSignal, ({ childWorkflowId, result }) => {
389
+ childResults.set(childWorkflowId, result);
390
+ });
391
+ const handler = async (args, context) => {
366
392
  const config = subagents.find((s) => s.agentName === args.subagent);
367
393
  if (!config) {
368
394
  throw new Error(
@@ -371,29 +397,77 @@ function createSubagentHandler(subagents) {
371
397
  }
372
398
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
373
399
  const { sandboxId: parentSandboxId } = context;
374
- const inheritSandbox = config.sandbox !== "own" && !!parentSandboxId;
400
+ const sandboxCfg = resolveSandboxConfig(config.sandbox);
401
+ if (sandboxCfg.source === "inherit" && !parentSandboxId) {
402
+ throw new Error(
403
+ `Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
404
+ );
405
+ }
406
+ const threadMode = config.thread ?? "new";
407
+ const allowsContinuation = threadMode !== "new";
408
+ const continuationThreadId = args.threadId && allowsContinuation ? args.threadId : void 0;
409
+ let thread;
410
+ if (continuationThreadId) {
411
+ thread = { mode: threadMode, threadId: continuationThreadId };
412
+ }
413
+ let sandbox;
414
+ if (sandboxCfg.source === "inherit" && parentSandboxId) {
415
+ sandbox = { mode: "inherit", sandboxId: parentSandboxId };
416
+ } else if (sandboxCfg.source === "own") {
417
+ const prevSbId = continuationThreadId ? threadSandboxes.get(continuationThreadId) : void 0;
418
+ if (prevSbId) {
419
+ sandbox = { mode: "fork", sandboxId: prevSbId };
420
+ }
421
+ }
375
422
  const workflowInput = {
376
- ...args.threadId && args.threadId !== null && config.allowThreadContinuation && {
377
- previousThreadId: args.threadId
378
- },
379
- ...inheritSandbox && { sandboxId: parentSandboxId }
423
+ ...thread && { thread },
424
+ ...sandbox && { sandbox },
425
+ ...sandboxCfg.shutdown && { sandboxShutdown: sandboxCfg.shutdown }
380
426
  };
427
+ const resolvedContext = config.context === void 0 ? void 0 : typeof config.context === "function" ? config.context() : config.context;
381
428
  const childOpts = {
382
429
  workflowId: childWorkflowId,
383
- args: config.context === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, config.context],
430
+ args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
384
431
  taskQueue: config.taskQueue ?? parentTaskQueue
385
432
  };
433
+ const childHandle = await workflow.startChild(config.workflow, childOpts);
434
+ const usesOwnSandbox = sandboxCfg.source === "own" || allowsContinuation && sandboxCfg.source !== "inherit";
435
+ if (usesOwnSandbox) {
436
+ pendingDestroys.set(childWorkflowId, childHandle);
437
+ }
438
+ await Promise.race([
439
+ workflow.condition(() => childResults.has(childWorkflowId)),
440
+ childHandle.result()
441
+ ]);
442
+ if (!childResults.has(childWorkflowId)) {
443
+ await workflow.condition(() => childResults.has(childWorkflowId));
444
+ }
445
+ const childResult = childResults.get(childWorkflowId);
446
+ childResults.delete(childWorkflowId);
447
+ if (!childResult) {
448
+ return {
449
+ toolResponse: "Subagent workflow did not signal a result",
450
+ data: null
451
+ };
452
+ }
386
453
  const {
387
454
  toolResponse,
388
455
  data,
389
456
  usage,
390
- threadId: childThreadId
391
- } = typeof config.workflow === "string" ? await workflow.executeChild(config.workflow, childOpts) : await workflow.executeChild(config.workflow, childOpts);
457
+ threadId: childThreadId,
458
+ sandboxId: childSandboxId,
459
+ metadata
460
+ } = childResult;
461
+ if (allowsContinuation && childSandboxId && childThreadId) {
462
+ threadSandboxes.set(childThreadId, childSandboxId);
463
+ }
392
464
  if (!toolResponse) {
393
465
  return {
394
466
  toolResponse: "Subagent workflow returned no response",
395
467
  data: null,
396
- ...usage && { usage }
468
+ ...usage && { usage },
469
+ ...childSandboxId && { sandboxId: childSandboxId },
470
+ ...metadata && { metadata }
397
471
  };
398
472
  }
399
473
  const validated = config.resultSchema ? config.resultSchema.safeParse(data) : null;
@@ -401,11 +475,13 @@ function createSubagentHandler(subagents) {
401
475
  return {
402
476
  toolResponse: `Subagent workflow returned invalid data: ${validated.error.message}`,
403
477
  data: null,
404
- ...usage && { usage }
478
+ ...usage && { usage },
479
+ ...childSandboxId && { sandboxId: childSandboxId },
480
+ ...metadata && { metadata }
405
481
  };
406
482
  }
407
483
  let finalToolResponse = toolResponse;
408
- if (config.allowThreadContinuation && childThreadId) {
484
+ if (allowsContinuation && childThreadId) {
409
485
  finalToolResponse = typeof toolResponse === "string" ? `${toolResponse}
410
486
 
411
487
  [${config.agentName} Thread ID: ${childThreadId}]` : toolResponse;
@@ -413,9 +489,22 @@ function createSubagentHandler(subagents) {
413
489
  return {
414
490
  toolResponse: finalToolResponse,
415
491
  data: validated ? validated.data : data,
416
- ...usage && { usage }
492
+ ...usage && { usage },
493
+ ...childSandboxId && { sandboxId: childSandboxId },
494
+ ...metadata && { metadata }
417
495
  };
418
496
  };
497
+ const destroySubagentSandboxes = async () => {
498
+ const handles = [...pendingDestroys.values()];
499
+ pendingDestroys.clear();
500
+ await Promise.all(
501
+ handles.map(async (handle) => {
502
+ await handle.signal(destroySandboxSignal);
503
+ await handle.result();
504
+ })
505
+ );
506
+ };
507
+ return { handler, destroySubagentSandboxes };
419
508
  }
420
509
 
421
510
  // src/lib/subagent/register.ts
@@ -429,12 +518,13 @@ function buildSubagentRegistration(subagents) {
429
518
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
430
519
  }
431
520
  const resolveSubagentName = (args) => args.subagent;
432
- return {
521
+ const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents);
522
+ const registration = {
433
523
  name: SUBAGENT_TOOL_NAME,
434
524
  enabled: () => getEnabled().length > 0,
435
525
  description: () => createSubagentTool(getEnabled()).description,
436
526
  schema: () => createSubagentTool(getEnabled()).schema,
437
- handler: createSubagentHandler(subagents),
527
+ handler,
438
528
  ...subagentHooksMap.size > 0 && {
439
529
  hooks: {
440
530
  onPreToolUse: async (ctx) => {
@@ -452,6 +542,7 @@ function buildSubagentRegistration(subagents) {
452
542
  }
453
543
  }
454
544
  };
545
+ return { registration, destroySubagentSandboxes };
455
546
  }
456
547
  var READ_SKILL_TOOL_NAME = "ReadSkill";
457
548
  function buildReadSkillDescription(skills) {
@@ -504,8 +595,7 @@ function buildSkillRegistration(skills) {
504
595
  handler: createReadSkillHandler(skills)
505
596
  };
506
597
  }
507
- var createSession = async ({
508
- threadId: providedThreadId,
598
+ async function createSession({
509
599
  agentName,
510
600
  maxTurns = 50,
511
601
  metadata = {},
@@ -518,24 +608,42 @@ var createSession = async ({
518
608
  processToolsInParallel = true,
519
609
  hooks = {},
520
610
  appendSystemPrompt = true,
521
- continueThread = false,
522
611
  waitForInputTimeout = "48h",
523
- sandbox: sandboxOps,
524
- sandboxId: inheritedSandboxId
525
- }) => {
526
- const sourceThreadId = continueThread ? providedThreadId : void 0;
527
- const threadId = continueThread && providedThreadId ? getShortId() : providedThreadId ?? getShortId();
612
+ sandboxOps,
613
+ thread: threadInit,
614
+ sandbox: sandboxInit,
615
+ sandboxShutdown = "destroy"
616
+ }) {
617
+ const threadMode = threadInit?.mode ?? "new";
618
+ let threadId;
619
+ let sourceThreadId;
620
+ switch (threadMode) {
621
+ case "new":
622
+ threadId = threadInit?.mode === "new" && threadInit.threadId ? threadInit.threadId : getShortId();
623
+ break;
624
+ case "continue":
625
+ threadId = threadInit.threadId;
626
+ break;
627
+ case "fork":
628
+ sourceThreadId = threadInit.threadId;
629
+ threadId = getShortId();
630
+ break;
631
+ }
528
632
  const {
529
633
  appendToolResult,
530
634
  appendHumanMessage,
531
635
  initializeThread,
532
636
  appendSystemMessage,
533
637
  forkThread
534
- } = threadOps ?? proxyDefaultThreadOps();
638
+ } = threadOps;
535
639
  const plugins = [];
640
+ let destroySubagentSandboxes;
536
641
  if (subagents) {
537
- const reg = buildSubagentRegistration(subagents);
538
- if (reg) plugins.push(reg);
642
+ const result = buildSubagentRegistration(subagents);
643
+ if (result) {
644
+ plugins.push(result.registration);
645
+ destroySubagentSandboxes = result.destroySubagentSandboxes;
646
+ }
539
647
  }
540
648
  if (skills) {
541
649
  const reg = buildSkillRegistration(skills);
@@ -583,11 +691,41 @@ var createSession = async ({
583
691
  stateManager.run();
584
692
  }
585
693
  );
586
- let sandboxId = inheritedSandboxId;
587
- const ownsSandbox = !sandboxId && !!sandboxOps;
588
- if (ownsSandbox) {
589
- const result = await sandboxOps.createSandbox({ id: threadId });
694
+ const sandboxMode = sandboxInit?.mode;
695
+ let sandboxId;
696
+ let sandboxOwned = false;
697
+ if (sandboxMode === "inherit") {
698
+ sandboxId = sandboxInit.sandboxId;
699
+ if (!sandboxOps) {
700
+ throw workflow.ApplicationFailure.create({
701
+ message: "sandboxId provided but no sandboxOps \u2014 cannot manage sandbox lifecycle",
702
+ nonRetryable: true
703
+ });
704
+ }
705
+ } else if (sandboxMode === "continue") {
706
+ if (!sandboxOps) {
707
+ throw workflow.ApplicationFailure.create({
708
+ message: "No sandboxOps provided \u2014 cannot continue sandbox",
709
+ nonRetryable: true
710
+ });
711
+ }
712
+ sandboxId = sandboxInit.sandboxId;
713
+ sandboxOwned = true;
714
+ } else if (sandboxMode === "fork") {
715
+ if (!sandboxOps) {
716
+ throw workflow.ApplicationFailure.create({
717
+ message: "No sandboxOps provided \u2014 cannot fork sandbox",
718
+ nonRetryable: true
719
+ });
720
+ }
721
+ sandboxId = await sandboxOps.forkSandbox(
722
+ sandboxInit.sandboxId
723
+ );
724
+ sandboxOwned = true;
725
+ } else if (sandboxOps) {
726
+ const result = await sandboxOps.createSandbox();
590
727
  sandboxId = result.sandboxId;
728
+ sandboxOwned = true;
591
729
  if (result.stateUpdate) {
592
730
  stateManager.mergeUpdate(result.stateUpdate);
593
731
  }
@@ -600,9 +738,9 @@ var createSession = async ({
600
738
  });
601
739
  }
602
740
  const systemPrompt = stateManager.getSystemPrompt();
603
- if (continueThread && sourceThreadId) {
741
+ if (threadMode === "fork" && sourceThreadId) {
604
742
  await forkThread(sourceThreadId, threadId);
605
- } else {
743
+ } else if (threadMode === "continue") ; else {
606
744
  if (appendSystemPrompt) {
607
745
  if (!systemPrompt || systemPrompt.trim() === "") {
608
746
  throw workflow.ApplicationFailure.create({
@@ -637,7 +775,8 @@ var createSession = async ({
637
775
  threadId,
638
776
  finalMessage: message,
639
777
  exitReason,
640
- usage: stateManager.getTotalUsage()
778
+ usage: stateManager.getTotalUsage(),
779
+ sandboxId
641
780
  };
642
781
  }
643
782
  const parsedToolCalls = [];
@@ -688,55 +827,40 @@ var createSession = async ({
688
827
  throw workflow.ApplicationFailure.fromError(error);
689
828
  } finally {
690
829
  await callSessionEnd(exitReason, stateManager.getTurns());
691
- if (ownsSandbox && sandboxId && sandboxOps) {
692
- await sandboxOps.destroySandbox(sandboxId);
830
+ if (sandboxOwned && sandboxId && sandboxOps) {
831
+ switch (sandboxShutdown) {
832
+ case "destroy":
833
+ await sandboxOps.destroySandbox(sandboxId);
834
+ break;
835
+ case "pause":
836
+ case "pause-until-parent-close":
837
+ await sandboxOps.pauseSandbox(sandboxId);
838
+ break;
839
+ }
840
+ }
841
+ if (destroySubagentSandboxes) {
842
+ await destroySubagentSandboxes();
693
843
  }
694
844
  }
695
845
  return {
696
846
  threadId,
697
847
  finalMessage: null,
698
848
  exitReason,
699
- usage: stateManager.getTotalUsage()
849
+ usage: stateManager.getTotalUsage(),
850
+ sandboxId
700
851
  };
701
852
  }
702
853
  };
703
- };
704
- function proxyDefaultThreadOps(options) {
705
- return workflow.proxyActivities(
706
- options ?? {
707
- startToCloseTimeout: "10s",
708
- retry: {
709
- maximumAttempts: 6,
710
- initialInterval: "5s",
711
- maximumInterval: "15m",
712
- backoffCoefficient: 4
713
- }
714
- }
715
- );
716
- }
717
- function proxySandboxOps(options) {
718
- return workflow.proxyActivities(
719
- options ?? {
720
- startToCloseTimeout: "30s",
721
- retry: {
722
- maximumAttempts: 3,
723
- initialInterval: "2s",
724
- maximumInterval: "30s",
725
- backoffCoefficient: 2
726
- }
727
- }
728
- );
729
854
  }
730
855
 
731
856
  // src/lib/workflow.ts
732
857
  function defineWorkflow(config, fn) {
733
858
  const workflow = async (input, workflowInput = {}) => {
734
859
  const sessionInput = {
735
- ...workflowInput.previousThreadId && {
736
- threadId: workflowInput.previousThreadId,
737
- continueThread: true
738
- },
739
- ...workflowInput.sandboxId && { sandboxId: workflowInput.sandboxId }
860
+ agentName: config.name,
861
+ sandboxShutdown: config.sandboxShutdown ?? "destroy",
862
+ ...workflowInput.thread && { thread: workflowInput.thread },
863
+ ...workflowInput.sandbox && { sandbox: workflowInput.sandbox }
740
864
  };
741
865
  return fn(input, sessionInput);
742
866
  };
@@ -994,22 +1118,58 @@ function defineSubagent(definition, overrides) {
994
1118
  ...overrides
995
1119
  };
996
1120
  }
997
-
998
- // src/lib/subagent/workflow.ts
999
1121
  function defineSubagentWorkflow(config, fn) {
1000
- const workflow = async (prompt, workflowInput, context) => {
1122
+ const workflow$1 = async (prompt, workflowInput, context) => {
1123
+ const effectiveShutdown = workflowInput.sandboxShutdown ?? config.sandboxShutdown ?? "destroy";
1001
1124
  const sessionInput = {
1002
1125
  agentName: config.name,
1003
- ...workflowInput.previousThreadId && {
1004
- threadId: workflowInput.previousThreadId,
1005
- continueThread: true
1006
- },
1007
- ...workflowInput.sandboxId && { sandboxId: workflowInput.sandboxId }
1126
+ sandboxShutdown: effectiveShutdown,
1127
+ ...workflowInput.thread && { thread: workflowInput.thread },
1128
+ ...workflowInput.sandbox && { sandbox: workflowInput.sandbox }
1008
1129
  };
1009
- return fn(prompt, sessionInput, context ?? {});
1130
+ const { destroySandbox, ...result } = await fn(
1131
+ prompt,
1132
+ sessionInput,
1133
+ context ?? {}
1134
+ );
1135
+ if (effectiveShutdown === "pause-until-parent-close") {
1136
+ if (!destroySandbox) {
1137
+ throw workflow.ApplicationFailure.create({
1138
+ message: `Subagent "${config.name}" has sandboxShutdown="pause-until-parent-close" but fn did not return a destroySandbox callback`,
1139
+ nonRetryable: true
1140
+ });
1141
+ }
1142
+ if (!result.sandboxId) {
1143
+ throw workflow.ApplicationFailure.create({
1144
+ message: `Subagent "${config.name}" has sandboxShutdown="pause-until-parent-close" but fn did not return a sandboxId`,
1145
+ nonRetryable: true
1146
+ });
1147
+ }
1148
+ }
1149
+ const { parent } = workflow.workflowInfo();
1150
+ if (!parent) {
1151
+ throw workflow.ApplicationFailure.create({
1152
+ message: "Subagent workflow called without a parent workflow",
1153
+ nonRetryable: true
1154
+ });
1155
+ }
1156
+ const parentHandle = workflow.getExternalWorkflowHandle(parent.workflowId);
1157
+ await parentHandle.signal(childResultSignal, {
1158
+ childWorkflowId: workflow.workflowInfo().workflowId,
1159
+ result
1160
+ });
1161
+ if (destroySandbox) {
1162
+ let destroyRequested = false;
1163
+ workflow.setHandler(destroySandboxSignal, () => {
1164
+ destroyRequested = true;
1165
+ });
1166
+ await workflow.condition(() => destroyRequested);
1167
+ await destroySandbox();
1168
+ }
1169
+ return result;
1010
1170
  };
1011
- Object.defineProperty(workflow, "name", { value: config.name });
1012
- return Object.assign(workflow, {
1171
+ Object.defineProperty(workflow$1, "name", { value: config.name });
1172
+ return Object.assign(workflow$1, {
1013
1173
  agentName: config.name,
1014
1174
  description: config.description,
1015
1175
  ...config.resultSchema !== void 0 && {
@@ -1741,6 +1901,9 @@ var SandboxManager = class {
1741
1901
  async destroy(id) {
1742
1902
  await this.provider.destroy(id);
1743
1903
  }
1904
+ async pause(id, ttlSeconds) {
1905
+ await this.provider.pause(id, ttlSeconds);
1906
+ }
1744
1907
  async snapshot(id) {
1745
1908
  return this.provider.snapshot(id);
1746
1909
  }
@@ -1748,22 +1911,53 @@ var SandboxManager = class {
1748
1911
  const sandbox = await this.provider.restore(snapshot);
1749
1912
  return sandbox.id;
1750
1913
  }
1914
+ async fork(sandboxId) {
1915
+ const sandbox = await this.provider.fork(sandboxId);
1916
+ return sandbox.id;
1917
+ }
1751
1918
  /**
1752
- * Returns Temporal activity functions matching {@link SandboxOps}.
1753
- * Spread these into your worker's activity map.
1919
+ * Returns Temporal activity functions with prefixed names.
1920
+ *
1921
+ * The provider's `id` is automatically prepended, so you only need
1922
+ * to pass the workflow/scope name. Use the matching `proxy*SandboxOps()`
1923
+ * helper from the adapter's `/workflow` entrypoint on the workflow side.
1924
+ *
1925
+ * @param scope - Workflow name (appended to the provider id)
1926
+ *
1927
+ * @example
1928
+ * ```typescript
1929
+ * const manager = new SandboxManager(new InMemorySandboxProvider());
1930
+ * manager.createActivities("CodingAgent");
1931
+ * // registers: inMemoryCodingAgentCreateSandbox, inMemoryCodingAgentDestroySandbox, …
1932
+ *
1933
+ * const vmgr = new SandboxManager(new VirtualSandboxProvider(resolver));
1934
+ * vmgr.createActivities("CodingAgent");
1935
+ * // registers: virtualCodingAgentCreateSandbox, …
1936
+ * ```
1754
1937
  */
1755
- createActivities() {
1756
- return {
1938
+ createActivities(scope) {
1939
+ const prefix = `${this.provider.id}${scope.charAt(0).toUpperCase()}${scope.slice(1)}`;
1940
+ const ops = {
1757
1941
  createSandbox: async (options) => {
1758
1942
  return this.create(options);
1759
1943
  },
1760
1944
  destroySandbox: async (sandboxId) => {
1761
1945
  await this.destroy(sandboxId);
1762
1946
  },
1947
+ pauseSandbox: async (sandboxId, ttlSeconds) => {
1948
+ await this.pause(sandboxId, ttlSeconds);
1949
+ },
1763
1950
  snapshotSandbox: async (sandboxId) => {
1764
1951
  return this.snapshot(sandboxId);
1952
+ },
1953
+ forkSandbox: async (sandboxId) => {
1954
+ return this.fork(sandboxId);
1765
1955
  }
1766
1956
  };
1957
+ const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
1958
+ return Object.fromEntries(
1959
+ Object.entries(ops).map(([k, v]) => [`${prefix}${cap(k)}`, v])
1960
+ );
1767
1961
  }
1768
1962
  };
1769
1963
 
@@ -2059,8 +2253,6 @@ exports.hasFileWithMimeType = hasFileWithMimeType;
2059
2253
  exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
2060
2254
  exports.isTerminalStatus = isTerminalStatus;
2061
2255
  exports.parseSkillFile = parseSkillFile;
2062
- exports.proxyDefaultThreadOps = proxyDefaultThreadOps;
2063
- exports.proxySandboxOps = proxySandboxOps;
2064
2256
  exports.queryParentWorkflowState = queryParentWorkflowState;
2065
2257
  exports.readFileHandler = readFileHandler;
2066
2258
  exports.readFileTool = readFileTool;