zeitlich 0.2.22 → 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 (101) hide show
  1. package/README.md +242 -59
  2. package/dist/adapters/sandbox/daytona/index.cjs +4 -1
  3. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  4. package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
  5. package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
  6. package/dist/adapters/sandbox/daytona/index.js +4 -1
  7. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  8. package/dist/adapters/sandbox/daytona/workflow.cjs +1 -0
  9. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  10. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  11. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  12. package/dist/adapters/sandbox/daytona/workflow.js +1 -0
  13. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  14. package/dist/adapters/sandbox/inmemory/index.cjs +16 -2
  15. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  16. package/dist/adapters/sandbox/inmemory/index.d.cts +3 -2
  17. package/dist/adapters/sandbox/inmemory/index.d.ts +3 -2
  18. package/dist/adapters/sandbox/inmemory/index.js +16 -2
  19. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  20. package/dist/adapters/sandbox/inmemory/workflow.cjs +1 -0
  21. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  22. package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
  23. package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
  24. package/dist/adapters/sandbox/inmemory/workflow.js +1 -0
  25. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  26. package/dist/adapters/sandbox/virtual/index.cjs +33 -9
  27. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/virtual/index.d.cts +6 -5
  29. package/dist/adapters/sandbox/virtual/index.d.ts +6 -5
  30. package/dist/adapters/sandbox/virtual/index.js +33 -9
  31. package/dist/adapters/sandbox/virtual/index.js.map +1 -1
  32. package/dist/adapters/sandbox/virtual/workflow.cjs +1 -0
  33. package/dist/adapters/sandbox/virtual/workflow.cjs.map +1 -1
  34. package/dist/adapters/sandbox/virtual/workflow.d.cts +3 -3
  35. package/dist/adapters/sandbox/virtual/workflow.d.ts +3 -3
  36. package/dist/adapters/sandbox/virtual/workflow.js +1 -0
  37. package/dist/adapters/sandbox/virtual/workflow.js.map +1 -1
  38. package/dist/adapters/thread/google-genai/index.d.cts +3 -3
  39. package/dist/adapters/thread/google-genai/index.d.ts +3 -3
  40. package/dist/adapters/thread/google-genai/workflow.d.cts +3 -3
  41. package/dist/adapters/thread/google-genai/workflow.d.ts +3 -3
  42. package/dist/adapters/thread/langchain/index.d.cts +3 -3
  43. package/dist/adapters/thread/langchain/index.d.ts +3 -3
  44. package/dist/adapters/thread/langchain/workflow.d.cts +3 -3
  45. package/dist/adapters/thread/langchain/workflow.d.ts +3 -3
  46. package/dist/index.cjs +247 -57
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.d.cts +9 -8
  49. package/dist/index.d.ts +9 -8
  50. package/dist/index.js +245 -55
  51. package/dist/index.js.map +1 -1
  52. package/dist/{queries-Bw6WEPMw.d.cts → queries-DModcWRy.d.cts} +1 -1
  53. package/dist/{queries-C27raDaB.d.ts → queries-byD0jr1Y.d.ts} +1 -1
  54. package/dist/{types-ClsHhtwL.d.cts → types-B50pBPEV.d.ts} +159 -35
  55. package/dist/{types-YbL7JpEA.d.cts → types-Bll19FZJ.d.cts} +7 -0
  56. package/dist/{types-YbL7JpEA.d.ts → types-Bll19FZJ.d.ts} +7 -0
  57. package/dist/{types-BJ8itUAl.d.cts → types-BuXdFhaZ.d.cts} +6 -6
  58. package/dist/{types-HBosetv3.d.cts → types-ChAMwU3q.d.cts} +2 -0
  59. package/dist/{types-HBosetv3.d.ts → types-ChAMwU3q.d.ts} +2 -0
  60. package/dist/{types-C5bkx6kQ.d.ts → types-DQW8l7pY.d.cts} +159 -35
  61. package/dist/{types-ENYCKFBk.d.ts → types-GZ76HZSj.d.ts} +6 -6
  62. package/dist/workflow.cjs +241 -57
  63. package/dist/workflow.cjs.map +1 -1
  64. package/dist/workflow.d.cts +49 -32
  65. package/dist/workflow.d.ts +49 -32
  66. package/dist/workflow.js +239 -55
  67. package/dist/workflow.js.map +1 -1
  68. package/package.json +2 -2
  69. package/src/adapters/sandbox/daytona/filesystem.ts +1 -1
  70. package/src/adapters/sandbox/daytona/index.ts +4 -0
  71. package/src/adapters/sandbox/daytona/proxy.ts +4 -3
  72. package/src/adapters/sandbox/e2b/index.ts +5 -0
  73. package/src/adapters/sandbox/inmemory/index.ts +24 -4
  74. package/src/adapters/sandbox/inmemory/proxy.ts +2 -2
  75. package/src/adapters/sandbox/virtual/filesystem.ts +41 -17
  76. package/src/adapters/sandbox/virtual/provider.ts +4 -0
  77. package/src/adapters/sandbox/virtual/proxy.ts +1 -0
  78. package/src/adapters/sandbox/virtual/types.ts +9 -4
  79. package/src/lib/lifecycle.ts +57 -0
  80. package/src/lib/sandbox/manager.ts +13 -1
  81. package/src/lib/sandbox/types.ts +13 -4
  82. package/src/lib/session/index.ts +1 -0
  83. package/src/lib/session/session-edge-cases.integration.test.ts +447 -33
  84. package/src/lib/session/session.integration.test.ts +52 -32
  85. package/src/lib/session/session.ts +107 -33
  86. package/src/lib/session/types.ts +55 -16
  87. package/src/lib/subagent/define.ts +5 -4
  88. package/src/lib/subagent/handler.ts +139 -14
  89. package/src/lib/subagent/index.ts +3 -0
  90. package/src/lib/subagent/register.ts +10 -3
  91. package/src/lib/subagent/signals.ts +8 -0
  92. package/src/lib/subagent/subagent.integration.test.ts +853 -150
  93. package/src/lib/subagent/tool.ts +2 -2
  94. package/src/lib/subagent/types.ts +77 -19
  95. package/src/lib/subagent/workflow.ts +83 -12
  96. package/src/lib/tool-router/router.integration.test.ts +137 -4
  97. package/src/lib/tool-router/router.ts +13 -3
  98. package/src/lib/tool-router/types.ts +7 -0
  99. package/src/lib/workflow.test.ts +89 -21
  100. package/src/lib/workflow.ts +33 -18
  101. package/src/workflow.ts +6 -1
package/dist/workflow.js CHANGED
@@ -1,4 +1,4 @@
1
- import { uuid4, setHandler, defineUpdate, ApplicationFailure, condition, defineQuery, workflowInfo, executeChild } from '@temporalio/workflow';
1
+ import { defineSignal, uuid4, setHandler, defineUpdate, ApplicationFailure, condition, defineQuery, workflowInfo, getExternalWorkflowHandle, startChild } from '@temporalio/workflow';
2
2
  import z14, { z } from 'zod';
3
3
  import { ApplicationFailure as ApplicationFailure$1 } from '@temporalio/common';
4
4
 
@@ -79,7 +79,12 @@ function createToolRouter(options) {
79
79
  result: { error: errorStr, suppressed: true }
80
80
  };
81
81
  }
82
- throw ApplicationFailure.fromError(error, { nonRetryable: true });
82
+ return {
83
+ content: JSON.stringify({
84
+ error: "The tool encountered an error. Please try again or use a different approach."
85
+ }),
86
+ result: { error: errorStr, suppressed: true }
87
+ };
83
88
  }
84
89
  async function runPostHooks(toolCall, tool, toolResult, effectiveArgs, turn, durationMs) {
85
90
  if (tool?.hooks?.onPostToolUse) {
@@ -88,7 +93,8 @@ function createToolRouter(options) {
88
93
  result: toolResult.data,
89
94
  threadId: options.threadId,
90
95
  turn,
91
- durationMs
96
+ durationMs,
97
+ ...toolResult.metadata && { metadata: toolResult.metadata }
92
98
  });
93
99
  }
94
100
  if (options.hooks?.onPostToolUse) {
@@ -121,6 +127,7 @@ function createToolRouter(options) {
121
127
  let result;
122
128
  let content;
123
129
  let resultAppended = false;
130
+ let metadata;
124
131
  try {
125
132
  if (tool) {
126
133
  const routerContext = {
@@ -136,6 +143,7 @@ function createToolRouter(options) {
136
143
  result = response.data;
137
144
  content = response.toolResponse;
138
145
  resultAppended = response.resultAppended === true;
146
+ metadata = response.metadata;
139
147
  } else {
140
148
  result = { error: `Unknown tool: ${toolCall.name}` };
141
149
  content = JSON.stringify(result, null, 2);
@@ -168,7 +176,8 @@ function createToolRouter(options) {
168
176
  const toolResult = {
169
177
  toolCallId: toolCall.id,
170
178
  name: toolCall.name,
171
- data: result
179
+ data: result,
180
+ ...metadata && { metadata }
172
181
  };
173
182
  await runPostHooks(
174
183
  toolCall,
@@ -272,7 +281,8 @@ function createToolRouter(options) {
272
281
  return {
273
282
  toolCallId: toolCall.id,
274
283
  name: toolCall.name,
275
- data: response.data
284
+ data: response.data,
285
+ ...response.metadata && { metadata: response.metadata }
276
286
  };
277
287
  };
278
288
  if (options.parallel) {
@@ -316,7 +326,7 @@ function getShortId(length = 12) {
316
326
  var SUBAGENT_TOOL_NAME = "Subagent";
317
327
  function buildSubagentDescription(subagents) {
318
328
  const subagentList = subagents.map((s) => {
319
- const continuation = s.allowThreadContinuation ? "\n*(Supports thread continuation \u2014 pass a threadId to resume a previous conversation)*" : "";
329
+ const continuation = s.thread && s.thread !== "new" ? "\n*(Supports thread continuation \u2014 pass a threadId to resume a previous conversation)*" : "";
320
330
  return `## ${s.agentName}
321
331
  ${s.description}${continuation}`;
322
332
  }).join("\n\n");
@@ -332,7 +342,7 @@ function createSubagentTool(subagents) {
332
342
  }
333
343
  const names = subagents.map((s) => s.agentName);
334
344
  const hasThreadContinuation = subagents.some(
335
- (s) => s.allowThreadContinuation
345
+ (s) => s.thread && s.thread !== "new"
336
346
  );
337
347
  const baseFields = {
338
348
  subagent: z14.enum(names).describe("The type of subagent to launch"),
@@ -351,9 +361,25 @@ function createSubagentTool(subagents) {
351
361
  schema
352
362
  };
353
363
  }
364
+ var childResultSignal = defineSignal("childResult");
365
+ var destroySandboxSignal = defineSignal("destroySandbox");
366
+
367
+ // src/lib/subagent/handler.ts
368
+ function resolveSandboxConfig(config) {
369
+ if (!config || config === "none") return { source: "none" };
370
+ if (config === "inherit") return { source: "inherit" };
371
+ if (config === "own") return { source: "own" };
372
+ return { source: "own", shutdown: config.shutdown };
373
+ }
354
374
  function createSubagentHandler(subagents) {
355
375
  const { taskQueue: parentTaskQueue } = workflowInfo();
356
- return async (args, context) => {
376
+ const childResults = /* @__PURE__ */ new Map();
377
+ const pendingDestroys = /* @__PURE__ */ new Map();
378
+ const threadSandboxes = /* @__PURE__ */ new Map();
379
+ setHandler(childResultSignal, ({ childWorkflowId, result }) => {
380
+ childResults.set(childWorkflowId, result);
381
+ });
382
+ const handler = async (args, context) => {
357
383
  const config = subagents.find((s) => s.agentName === args.subagent);
358
384
  if (!config) {
359
385
  throw new Error(
@@ -362,12 +388,32 @@ function createSubagentHandler(subagents) {
362
388
  }
363
389
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
364
390
  const { sandboxId: parentSandboxId } = context;
365
- const inheritSandbox = config.sandbox !== "own" && !!parentSandboxId;
391
+ const sandboxCfg = resolveSandboxConfig(config.sandbox);
392
+ if (sandboxCfg.source === "inherit" && !parentSandboxId) {
393
+ throw new Error(
394
+ `Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
395
+ );
396
+ }
397
+ const threadMode = config.thread ?? "new";
398
+ const allowsContinuation = threadMode !== "new";
399
+ const continuationThreadId = args.threadId && allowsContinuation ? args.threadId : void 0;
400
+ let thread;
401
+ if (continuationThreadId) {
402
+ thread = { mode: threadMode, threadId: continuationThreadId };
403
+ }
404
+ let sandbox;
405
+ if (sandboxCfg.source === "inherit" && parentSandboxId) {
406
+ sandbox = { mode: "inherit", sandboxId: parentSandboxId };
407
+ } else if (sandboxCfg.source === "own") {
408
+ const prevSbId = continuationThreadId ? threadSandboxes.get(continuationThreadId) : void 0;
409
+ if (prevSbId) {
410
+ sandbox = { mode: "fork", sandboxId: prevSbId };
411
+ }
412
+ }
366
413
  const workflowInput = {
367
- ...args.threadId && args.threadId !== null && config.allowThreadContinuation && {
368
- previousThreadId: args.threadId
369
- },
370
- ...inheritSandbox && { sandboxId: parentSandboxId }
414
+ ...thread && { thread },
415
+ ...sandbox && { sandbox },
416
+ ...sandboxCfg.shutdown && { sandboxShutdown: sandboxCfg.shutdown }
371
417
  };
372
418
  const resolvedContext = config.context === void 0 ? void 0 : typeof config.context === "function" ? config.context() : config.context;
373
419
  const childOpts = {
@@ -375,17 +421,44 @@ function createSubagentHandler(subagents) {
375
421
  args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
376
422
  taskQueue: config.taskQueue ?? parentTaskQueue
377
423
  };
424
+ const childHandle = await startChild(config.workflow, childOpts);
425
+ const usesOwnSandbox = sandboxCfg.source === "own" || allowsContinuation && sandboxCfg.source !== "inherit";
426
+ if (usesOwnSandbox) {
427
+ pendingDestroys.set(childWorkflowId, childHandle);
428
+ }
429
+ await Promise.race([
430
+ condition(() => childResults.has(childWorkflowId)),
431
+ childHandle.result()
432
+ ]);
433
+ if (!childResults.has(childWorkflowId)) {
434
+ await condition(() => childResults.has(childWorkflowId));
435
+ }
436
+ const childResult = childResults.get(childWorkflowId);
437
+ childResults.delete(childWorkflowId);
438
+ if (!childResult) {
439
+ return {
440
+ toolResponse: "Subagent workflow did not signal a result",
441
+ data: null
442
+ };
443
+ }
378
444
  const {
379
445
  toolResponse,
380
446
  data,
381
447
  usage,
382
- threadId: childThreadId
383
- } = typeof config.workflow === "string" ? await executeChild(config.workflow, childOpts) : await executeChild(config.workflow, childOpts);
448
+ threadId: childThreadId,
449
+ sandboxId: childSandboxId,
450
+ metadata
451
+ } = childResult;
452
+ if (allowsContinuation && childSandboxId && childThreadId) {
453
+ threadSandboxes.set(childThreadId, childSandboxId);
454
+ }
384
455
  if (!toolResponse) {
385
456
  return {
386
457
  toolResponse: "Subagent workflow returned no response",
387
458
  data: null,
388
- ...usage && { usage }
459
+ ...usage && { usage },
460
+ ...childSandboxId && { sandboxId: childSandboxId },
461
+ ...metadata && { metadata }
389
462
  };
390
463
  }
391
464
  const validated = config.resultSchema ? config.resultSchema.safeParse(data) : null;
@@ -393,11 +466,13 @@ function createSubagentHandler(subagents) {
393
466
  return {
394
467
  toolResponse: `Subagent workflow returned invalid data: ${validated.error.message}`,
395
468
  data: null,
396
- ...usage && { usage }
469
+ ...usage && { usage },
470
+ ...childSandboxId && { sandboxId: childSandboxId },
471
+ ...metadata && { metadata }
397
472
  };
398
473
  }
399
474
  let finalToolResponse = toolResponse;
400
- if (config.allowThreadContinuation && childThreadId) {
475
+ if (allowsContinuation && childThreadId) {
401
476
  finalToolResponse = typeof toolResponse === "string" ? `${toolResponse}
402
477
 
403
478
  [${config.agentName} Thread ID: ${childThreadId}]` : toolResponse;
@@ -405,9 +480,22 @@ function createSubagentHandler(subagents) {
405
480
  return {
406
481
  toolResponse: finalToolResponse,
407
482
  data: validated ? validated.data : data,
408
- ...usage && { usage }
483
+ ...usage && { usage },
484
+ ...childSandboxId && { sandboxId: childSandboxId },
485
+ ...metadata && { metadata }
409
486
  };
410
487
  };
488
+ const destroySubagentSandboxes = async () => {
489
+ const handles = [...pendingDestroys.values()];
490
+ pendingDestroys.clear();
491
+ await Promise.all(
492
+ handles.map(async (handle) => {
493
+ await handle.signal(destroySandboxSignal);
494
+ await handle.result();
495
+ })
496
+ );
497
+ };
498
+ return { handler, destroySubagentSandboxes };
411
499
  }
412
500
 
413
501
  // src/lib/subagent/register.ts
@@ -421,12 +509,13 @@ function buildSubagentRegistration(subagents) {
421
509
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
422
510
  }
423
511
  const resolveSubagentName = (args) => args.subagent;
424
- return {
512
+ const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents);
513
+ const registration = {
425
514
  name: SUBAGENT_TOOL_NAME,
426
515
  enabled: () => getEnabled().length > 0,
427
516
  description: () => createSubagentTool(getEnabled()).description,
428
517
  schema: () => createSubagentTool(getEnabled()).schema,
429
- handler: createSubagentHandler(subagents),
518
+ handler,
430
519
  ...subagentHooksMap.size > 0 && {
431
520
  hooks: {
432
521
  onPreToolUse: async (ctx) => {
@@ -444,6 +533,7 @@ function buildSubagentRegistration(subagents) {
444
533
  }
445
534
  }
446
535
  };
536
+ return { registration, destroySubagentSandboxes };
447
537
  }
448
538
  var READ_SKILL_TOOL_NAME = "ReadSkill";
449
539
  function buildReadSkillDescription(skills) {
@@ -496,8 +586,7 @@ function buildSkillRegistration(skills) {
496
586
  handler: createReadSkillHandler(skills)
497
587
  };
498
588
  }
499
- var createSession = async ({
500
- threadId: providedThreadId,
589
+ async function createSession({
501
590
  agentName,
502
591
  maxTurns = 50,
503
592
  metadata = {},
@@ -510,13 +599,27 @@ var createSession = async ({
510
599
  processToolsInParallel = true,
511
600
  hooks = {},
512
601
  appendSystemPrompt = true,
513
- continueThread = false,
514
602
  waitForInputTimeout = "48h",
515
- sandbox: sandboxOps,
516
- sandboxId: inheritedSandboxId
517
- }) => {
518
- const sourceThreadId = continueThread ? providedThreadId : void 0;
519
- const threadId = continueThread && providedThreadId ? getShortId() : providedThreadId ?? getShortId();
603
+ sandboxOps,
604
+ thread: threadInit,
605
+ sandbox: sandboxInit,
606
+ sandboxShutdown = "destroy"
607
+ }) {
608
+ const threadMode = threadInit?.mode ?? "new";
609
+ let threadId;
610
+ let sourceThreadId;
611
+ switch (threadMode) {
612
+ case "new":
613
+ threadId = threadInit?.mode === "new" && threadInit.threadId ? threadInit.threadId : getShortId();
614
+ break;
615
+ case "continue":
616
+ threadId = threadInit.threadId;
617
+ break;
618
+ case "fork":
619
+ sourceThreadId = threadInit.threadId;
620
+ threadId = getShortId();
621
+ break;
622
+ }
520
623
  const {
521
624
  appendToolResult,
522
625
  appendHumanMessage,
@@ -525,9 +628,13 @@ var createSession = async ({
525
628
  forkThread
526
629
  } = threadOps;
527
630
  const plugins = [];
631
+ let destroySubagentSandboxes;
528
632
  if (subagents) {
529
- const reg = buildSubagentRegistration(subagents);
530
- if (reg) plugins.push(reg);
633
+ const result = buildSubagentRegistration(subagents);
634
+ if (result) {
635
+ plugins.push(result.registration);
636
+ destroySubagentSandboxes = result.destroySubagentSandboxes;
637
+ }
531
638
  }
532
639
  if (skills) {
533
640
  const reg = buildSkillRegistration(skills);
@@ -575,11 +682,41 @@ var createSession = async ({
575
682
  stateManager.run();
576
683
  }
577
684
  );
578
- let sandboxId = inheritedSandboxId;
579
- const ownsSandbox = !sandboxId && !!sandboxOps;
580
- if (ownsSandbox) {
581
- const result = await sandboxOps.createSandbox({ id: threadId });
685
+ const sandboxMode = sandboxInit?.mode;
686
+ let sandboxId;
687
+ let sandboxOwned = false;
688
+ if (sandboxMode === "inherit") {
689
+ sandboxId = sandboxInit.sandboxId;
690
+ if (!sandboxOps) {
691
+ throw ApplicationFailure.create({
692
+ message: "sandboxId provided but no sandboxOps \u2014 cannot manage sandbox lifecycle",
693
+ nonRetryable: true
694
+ });
695
+ }
696
+ } else if (sandboxMode === "continue") {
697
+ if (!sandboxOps) {
698
+ throw ApplicationFailure.create({
699
+ message: "No sandboxOps provided \u2014 cannot continue sandbox",
700
+ nonRetryable: true
701
+ });
702
+ }
703
+ sandboxId = sandboxInit.sandboxId;
704
+ sandboxOwned = true;
705
+ } else if (sandboxMode === "fork") {
706
+ if (!sandboxOps) {
707
+ throw ApplicationFailure.create({
708
+ message: "No sandboxOps provided \u2014 cannot fork sandbox",
709
+ nonRetryable: true
710
+ });
711
+ }
712
+ sandboxId = await sandboxOps.forkSandbox(
713
+ sandboxInit.sandboxId
714
+ );
715
+ sandboxOwned = true;
716
+ } else if (sandboxOps) {
717
+ const result = await sandboxOps.createSandbox();
582
718
  sandboxId = result.sandboxId;
719
+ sandboxOwned = true;
583
720
  if (result.stateUpdate) {
584
721
  stateManager.mergeUpdate(result.stateUpdate);
585
722
  }
@@ -592,9 +729,9 @@ var createSession = async ({
592
729
  });
593
730
  }
594
731
  const systemPrompt = stateManager.getSystemPrompt();
595
- if (continueThread && sourceThreadId) {
732
+ if (threadMode === "fork" && sourceThreadId) {
596
733
  await forkThread(sourceThreadId, threadId);
597
- } else {
734
+ } else if (threadMode === "continue") ; else {
598
735
  if (appendSystemPrompt) {
599
736
  if (!systemPrompt || systemPrompt.trim() === "") {
600
737
  throw ApplicationFailure.create({
@@ -629,7 +766,8 @@ var createSession = async ({
629
766
  threadId,
630
767
  finalMessage: message,
631
768
  exitReason,
632
- usage: stateManager.getTotalUsage()
769
+ usage: stateManager.getTotalUsage(),
770
+ sandboxId
633
771
  };
634
772
  }
635
773
  const parsedToolCalls = [];
@@ -680,30 +818,40 @@ var createSession = async ({
680
818
  throw ApplicationFailure.fromError(error);
681
819
  } finally {
682
820
  await callSessionEnd(exitReason, stateManager.getTurns());
683
- if (ownsSandbox && sandboxId && sandboxOps) {
684
- await sandboxOps.destroySandbox(sandboxId);
821
+ if (sandboxOwned && sandboxId && sandboxOps) {
822
+ switch (sandboxShutdown) {
823
+ case "destroy":
824
+ await sandboxOps.destroySandbox(sandboxId);
825
+ break;
826
+ case "pause":
827
+ case "pause-until-parent-close":
828
+ await sandboxOps.pauseSandbox(sandboxId);
829
+ break;
830
+ }
831
+ }
832
+ if (destroySubagentSandboxes) {
833
+ await destroySubagentSandboxes();
685
834
  }
686
835
  }
687
836
  return {
688
837
  threadId,
689
838
  finalMessage: null,
690
839
  exitReason,
691
- usage: stateManager.getTotalUsage()
840
+ usage: stateManager.getTotalUsage(),
841
+ sandboxId
692
842
  };
693
843
  }
694
844
  };
695
- };
845
+ }
696
846
 
697
847
  // src/lib/workflow.ts
698
848
  function defineWorkflow(config, fn) {
699
849
  const workflow = async (input, workflowInput = {}) => {
700
850
  const sessionInput = {
701
851
  agentName: config.name,
702
- ...workflowInput.previousThreadId && {
703
- threadId: workflowInput.previousThreadId,
704
- continueThread: true
705
- },
706
- ...workflowInput.sandboxId && { sandboxId: workflowInput.sandboxId }
852
+ sandboxShutdown: config.sandboxShutdown ?? "destroy",
853
+ ...workflowInput.thread && { thread: workflowInput.thread },
854
+ ...workflowInput.sandbox && { sandbox: workflowInput.sandbox }
707
855
  };
708
856
  return fn(input, sessionInput);
709
857
  };
@@ -884,19 +1032,55 @@ function defineSubagent(definition, overrides) {
884
1032
  ...overrides
885
1033
  };
886
1034
  }
887
-
888
- // src/lib/subagent/workflow.ts
889
1035
  function defineSubagentWorkflow(config, fn) {
890
1036
  const workflow = async (prompt, workflowInput, context) => {
1037
+ const effectiveShutdown = workflowInput.sandboxShutdown ?? config.sandboxShutdown ?? "destroy";
891
1038
  const sessionInput = {
892
1039
  agentName: config.name,
893
- ...workflowInput.previousThreadId && {
894
- threadId: workflowInput.previousThreadId,
895
- continueThread: true
896
- },
897
- ...workflowInput.sandboxId && { sandboxId: workflowInput.sandboxId }
1040
+ sandboxShutdown: effectiveShutdown,
1041
+ ...workflowInput.thread && { thread: workflowInput.thread },
1042
+ ...workflowInput.sandbox && { sandbox: workflowInput.sandbox }
898
1043
  };
899
- return fn(prompt, sessionInput, context ?? {});
1044
+ const { destroySandbox, ...result } = await fn(
1045
+ prompt,
1046
+ sessionInput,
1047
+ context ?? {}
1048
+ );
1049
+ if (effectiveShutdown === "pause-until-parent-close") {
1050
+ if (!destroySandbox) {
1051
+ throw ApplicationFailure.create({
1052
+ message: `Subagent "${config.name}" has sandboxShutdown="pause-until-parent-close" but fn did not return a destroySandbox callback`,
1053
+ nonRetryable: true
1054
+ });
1055
+ }
1056
+ if (!result.sandboxId) {
1057
+ throw ApplicationFailure.create({
1058
+ message: `Subagent "${config.name}" has sandboxShutdown="pause-until-parent-close" but fn did not return a sandboxId`,
1059
+ nonRetryable: true
1060
+ });
1061
+ }
1062
+ }
1063
+ const { parent } = workflowInfo();
1064
+ if (!parent) {
1065
+ throw ApplicationFailure.create({
1066
+ message: "Subagent workflow called without a parent workflow",
1067
+ nonRetryable: true
1068
+ });
1069
+ }
1070
+ const parentHandle = getExternalWorkflowHandle(parent.workflowId);
1071
+ await parentHandle.signal(childResultSignal, {
1072
+ childWorkflowId: workflowInfo().workflowId,
1073
+ result
1074
+ });
1075
+ if (destroySandbox) {
1076
+ let destroyRequested = false;
1077
+ setHandler(destroySandboxSignal, () => {
1078
+ destroyRequested = true;
1079
+ });
1080
+ await condition(() => destroyRequested);
1081
+ await destroySandbox();
1082
+ }
1083
+ return result;
900
1084
  };
901
1085
  Object.defineProperty(workflow, "name", { value: config.name });
902
1086
  return Object.assign(workflow, {