zeitlich 0.2.22 → 0.2.24

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 +278 -59
  2. package/dist/adapters/sandbox/bedrock/index.cjs +427 -0
  3. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -0
  4. package/dist/adapters/sandbox/bedrock/index.d.cts +23 -0
  5. package/dist/adapters/sandbox/bedrock/index.d.ts +23 -0
  6. package/dist/adapters/sandbox/bedrock/index.js +424 -0
  7. package/dist/adapters/sandbox/bedrock/index.js.map +1 -0
  8. package/dist/adapters/sandbox/bedrock/workflow.cjs +33 -0
  9. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -0
  10. package/dist/adapters/sandbox/bedrock/workflow.d.cts +29 -0
  11. package/dist/adapters/sandbox/bedrock/workflow.d.ts +29 -0
  12. package/dist/adapters/sandbox/bedrock/workflow.js +31 -0
  13. package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -0
  14. package/dist/adapters/sandbox/daytona/index.cjs +4 -1
  15. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  16. package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
  17. package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
  18. package/dist/adapters/sandbox/daytona/index.js +4 -1
  19. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  20. package/dist/adapters/sandbox/daytona/workflow.cjs +1 -0
  21. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  22. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  23. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  24. package/dist/adapters/sandbox/daytona/workflow.js +1 -0
  25. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  26. package/dist/adapters/sandbox/inmemory/index.cjs +16 -2
  27. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  28. package/dist/adapters/sandbox/inmemory/index.d.cts +3 -2
  29. package/dist/adapters/sandbox/inmemory/index.d.ts +3 -2
  30. package/dist/adapters/sandbox/inmemory/index.js +16 -2
  31. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  32. package/dist/adapters/sandbox/inmemory/workflow.cjs +1 -0
  33. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  34. package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
  35. package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
  36. package/dist/adapters/sandbox/inmemory/workflow.js +1 -0
  37. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  38. package/dist/adapters/sandbox/virtual/index.cjs +45 -11
  39. package/dist/adapters/sandbox/virtual/index.cjs.map +1 -1
  40. package/dist/adapters/sandbox/virtual/index.d.cts +6 -5
  41. package/dist/adapters/sandbox/virtual/index.d.ts +6 -5
  42. package/dist/adapters/sandbox/virtual/index.js +45 -11
  43. package/dist/adapters/sandbox/virtual/index.js.map +1 -1
  44. package/dist/adapters/sandbox/virtual/workflow.cjs +1 -0
  45. package/dist/adapters/sandbox/virtual/workflow.cjs.map +1 -1
  46. package/dist/adapters/sandbox/virtual/workflow.d.cts +3 -3
  47. package/dist/adapters/sandbox/virtual/workflow.d.ts +3 -3
  48. package/dist/adapters/sandbox/virtual/workflow.js +1 -0
  49. package/dist/adapters/sandbox/virtual/workflow.js.map +1 -1
  50. package/dist/adapters/thread/google-genai/index.d.cts +3 -3
  51. package/dist/adapters/thread/google-genai/index.d.ts +3 -3
  52. package/dist/adapters/thread/google-genai/workflow.d.cts +3 -3
  53. package/dist/adapters/thread/google-genai/workflow.d.ts +3 -3
  54. package/dist/adapters/thread/langchain/index.d.cts +3 -3
  55. package/dist/adapters/thread/langchain/index.d.ts +3 -3
  56. package/dist/adapters/thread/langchain/workflow.d.cts +3 -3
  57. package/dist/adapters/thread/langchain/workflow.d.ts +3 -3
  58. package/dist/index.cjs +443 -71
  59. package/dist/index.cjs.map +1 -1
  60. package/dist/index.d.cts +64 -10
  61. package/dist/index.d.ts +64 -10
  62. package/dist/index.js +442 -71
  63. package/dist/index.js.map +1 -1
  64. package/dist/{queries-Bw6WEPMw.d.cts → queries-BYGBImeC.d.cts} +1 -1
  65. package/dist/{queries-C27raDaB.d.ts → queries-DwBe2CAA.d.ts} +1 -1
  66. package/dist/{types-C5bkx6kQ.d.ts → types-7PeMi1bD.d.cts} +167 -36
  67. package/dist/{types-BJ8itUAl.d.cts → types-Bf8KV0Ci.d.cts} +6 -6
  68. package/dist/{types-HBosetv3.d.cts → types-ChAMwU3q.d.cts} +2 -0
  69. package/dist/{types-HBosetv3.d.ts → types-ChAMwU3q.d.ts} +2 -0
  70. package/dist/{types-YbL7JpEA.d.cts → types-D_igp10o.d.cts} +11 -0
  71. package/dist/{types-YbL7JpEA.d.ts → types-D_igp10o.d.ts} +11 -0
  72. package/dist/types-DhTCEMhr.d.cts +64 -0
  73. package/dist/{types-ENYCKFBk.d.ts → types-LVKmCNds.d.ts} +6 -6
  74. package/dist/types-d9NznUqd.d.ts +64 -0
  75. package/dist/{types-ClsHhtwL.d.cts → types-hmferhc2.d.ts} +167 -36
  76. package/dist/workflow.cjs +308 -63
  77. package/dist/workflow.cjs.map +1 -1
  78. package/dist/workflow.d.cts +54 -32
  79. package/dist/workflow.d.ts +54 -32
  80. package/dist/workflow.js +306 -61
  81. package/dist/workflow.js.map +1 -1
  82. package/package.json +27 -2
  83. package/src/adapters/sandbox/bedrock/filesystem.ts +313 -0
  84. package/src/adapters/sandbox/bedrock/index.ts +259 -0
  85. package/src/adapters/sandbox/bedrock/proxy.ts +56 -0
  86. package/src/adapters/sandbox/bedrock/types.ts +24 -0
  87. package/src/adapters/sandbox/daytona/filesystem.ts +1 -1
  88. package/src/adapters/sandbox/daytona/index.ts +4 -0
  89. package/src/adapters/sandbox/daytona/proxy.ts +4 -3
  90. package/src/adapters/sandbox/e2b/index.ts +5 -0
  91. package/src/adapters/sandbox/inmemory/index.ts +24 -4
  92. package/src/adapters/sandbox/inmemory/proxy.ts +2 -2
  93. package/src/adapters/sandbox/virtual/filesystem.ts +44 -18
  94. package/src/adapters/sandbox/virtual/provider.ts +13 -0
  95. package/src/adapters/sandbox/virtual/proxy.ts +1 -0
  96. package/src/adapters/sandbox/virtual/types.ts +9 -4
  97. package/src/adapters/sandbox/virtual/virtual-sandbox.test.ts +26 -0
  98. package/src/index.ts +2 -1
  99. package/src/lib/lifecycle.ts +57 -0
  100. package/src/lib/sandbox/manager.ts +13 -1
  101. package/src/lib/sandbox/node-fs.ts +115 -0
  102. package/src/lib/sandbox/types.ts +13 -4
  103. package/src/lib/session/index.ts +1 -0
  104. package/src/lib/session/session-edge-cases.integration.test.ts +447 -33
  105. package/src/lib/session/session.integration.test.ts +149 -32
  106. package/src/lib/session/session.ts +138 -33
  107. package/src/lib/session/types.ts +56 -17
  108. package/src/lib/skills/fs-provider.ts +65 -4
  109. package/src/lib/skills/handler.ts +43 -1
  110. package/src/lib/skills/index.ts +0 -1
  111. package/src/lib/skills/register.ts +17 -1
  112. package/src/lib/skills/skills.integration.test.ts +308 -24
  113. package/src/lib/skills/types.ts +6 -0
  114. package/src/lib/subagent/define.ts +5 -4
  115. package/src/lib/subagent/handler.ts +143 -14
  116. package/src/lib/subagent/index.ts +3 -0
  117. package/src/lib/subagent/register.ts +10 -3
  118. package/src/lib/subagent/signals.ts +8 -0
  119. package/src/lib/subagent/subagent.integration.test.ts +853 -150
  120. package/src/lib/subagent/tool.ts +2 -2
  121. package/src/lib/subagent/types.ts +77 -19
  122. package/src/lib/subagent/workflow.ts +83 -12
  123. package/src/lib/tool-router/router.integration.test.ts +137 -4
  124. package/src/lib/tool-router/router.ts +19 -6
  125. package/src/lib/tool-router/types.ts +11 -0
  126. package/src/lib/workflow.test.ts +89 -21
  127. package/src/lib/workflow.ts +33 -18
  128. package/src/workflow.ts +6 -1
  129. package/tsup.config.ts +3 -0
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) {
@@ -101,7 +107,7 @@ function createToolRouter(options) {
101
107
  });
102
108
  }
103
109
  }
104
- async function processToolCall(toolCall, turn, sandboxId) {
110
+ async function processToolCall(toolCall, turn, sandboxId, sandboxStateUpdate) {
105
111
  const startTime = Date.now();
106
112
  const tool = toolMap.get(toolCall.name);
107
113
  const preResult = await runPreHooks(toolCall, tool, turn);
@@ -121,13 +127,15 @@ 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 = {
127
134
  threadId: options.threadId,
128
135
  toolCallId: toolCall.id,
129
136
  toolName: toolCall.name,
130
- ...sandboxId !== void 0 && { sandboxId }
137
+ ...sandboxId !== void 0 && { sandboxId },
138
+ ...sandboxStateUpdate && { sandboxStateUpdate }
131
139
  };
132
140
  const response = await tool.handler(
133
141
  effectiveArgs,
@@ -136,6 +144,7 @@ function createToolRouter(options) {
136
144
  result = response.data;
137
145
  content = response.toolResponse;
138
146
  resultAppended = response.resultAppended === true;
147
+ metadata = response.metadata;
139
148
  } else {
140
149
  result = { error: `Unknown tool: ${toolCall.name}` };
141
150
  content = JSON.stringify(result, null, 2);
@@ -168,7 +177,8 @@ function createToolRouter(options) {
168
177
  const toolResult = {
169
178
  toolCallId: toolCall.id,
170
179
  name: toolCall.name,
171
- data: result
180
+ data: result,
181
+ ...metadata && { metadata }
172
182
  };
173
183
  await runPostHooks(
174
184
  toolCall,
@@ -218,9 +228,10 @@ function createToolRouter(options) {
218
228
  }
219
229
  const turn = context?.turn ?? 0;
220
230
  const sandboxId = context?.sandboxId;
231
+ const sandboxStateUpdate = context?.sandboxStateUpdate;
221
232
  if (options.parallel) {
222
233
  const results2 = await Promise.all(
223
- toolCalls.map((tc) => processToolCall(tc, turn, sandboxId))
234
+ toolCalls.map((tc) => processToolCall(tc, turn, sandboxId, sandboxStateUpdate))
224
235
  );
225
236
  return results2.filter(
226
237
  (r) => r !== null
@@ -228,7 +239,7 @@ function createToolRouter(options) {
228
239
  }
229
240
  const results = [];
230
241
  for (const toolCall of toolCalls) {
231
- const result = await processToolCall(toolCall, turn, sandboxId);
242
+ const result = await processToolCall(toolCall, turn, sandboxId, sandboxStateUpdate);
232
243
  if (result !== null) {
233
244
  results.push(result);
234
245
  }
@@ -272,7 +283,8 @@ function createToolRouter(options) {
272
283
  return {
273
284
  toolCallId: toolCall.id,
274
285
  name: toolCall.name,
275
- data: response.data
286
+ data: response.data,
287
+ ...response.metadata && { metadata: response.metadata }
276
288
  };
277
289
  };
278
290
  if (options.parallel) {
@@ -316,7 +328,7 @@ function getShortId(length = 12) {
316
328
  var SUBAGENT_TOOL_NAME = "Subagent";
317
329
  function buildSubagentDescription(subagents) {
318
330
  const subagentList = subagents.map((s) => {
319
- const continuation = s.allowThreadContinuation ? "\n*(Supports thread continuation \u2014 pass a threadId to resume a previous conversation)*" : "";
331
+ const continuation = s.thread && s.thread !== "new" ? "\n*(Supports thread continuation \u2014 pass a threadId to resume a previous conversation)*" : "";
320
332
  return `## ${s.agentName}
321
333
  ${s.description}${continuation}`;
322
334
  }).join("\n\n");
@@ -332,7 +344,7 @@ function createSubagentTool(subagents) {
332
344
  }
333
345
  const names = subagents.map((s) => s.agentName);
334
346
  const hasThreadContinuation = subagents.some(
335
- (s) => s.allowThreadContinuation
347
+ (s) => s.thread && s.thread !== "new"
336
348
  );
337
349
  const baseFields = {
338
350
  subagent: z14.enum(names).describe("The type of subagent to launch"),
@@ -351,9 +363,25 @@ function createSubagentTool(subagents) {
351
363
  schema
352
364
  };
353
365
  }
366
+ var childResultSignal = defineSignal("childResult");
367
+ var destroySandboxSignal = defineSignal("destroySandbox");
368
+
369
+ // src/lib/subagent/handler.ts
370
+ function resolveSandboxConfig(config) {
371
+ if (!config || config === "none") return { source: "none" };
372
+ if (config === "inherit") return { source: "inherit" };
373
+ if (config === "own") return { source: "own" };
374
+ return { source: "own", shutdown: config.shutdown };
375
+ }
354
376
  function createSubagentHandler(subagents) {
355
377
  const { taskQueue: parentTaskQueue } = workflowInfo();
356
- return async (args, context) => {
378
+ const childResults = /* @__PURE__ */ new Map();
379
+ const pendingDestroys = /* @__PURE__ */ new Map();
380
+ const threadSandboxes = /* @__PURE__ */ new Map();
381
+ setHandler(childResultSignal, ({ childWorkflowId, result }) => {
382
+ childResults.set(childWorkflowId, result);
383
+ });
384
+ const handler = async (args, context) => {
357
385
  const config = subagents.find((s) => s.agentName === args.subagent);
358
386
  if (!config) {
359
387
  throw new Error(
@@ -362,12 +390,36 @@ function createSubagentHandler(subagents) {
362
390
  }
363
391
  const childWorkflowId = `${args.subagent}-${getShortId()}`;
364
392
  const { sandboxId: parentSandboxId } = context;
365
- const inheritSandbox = config.sandbox !== "own" && !!parentSandboxId;
393
+ const sandboxCfg = resolveSandboxConfig(config.sandbox);
394
+ if (sandboxCfg.source === "inherit" && !parentSandboxId) {
395
+ throw new Error(
396
+ `Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
397
+ );
398
+ }
399
+ const threadMode = config.thread ?? "new";
400
+ const allowsContinuation = threadMode !== "new";
401
+ const continuationThreadId = args.threadId && allowsContinuation ? args.threadId : void 0;
402
+ let thread;
403
+ if (continuationThreadId) {
404
+ thread = { mode: threadMode, threadId: continuationThreadId };
405
+ }
406
+ let sandbox;
407
+ if (sandboxCfg.source === "inherit" && parentSandboxId) {
408
+ sandbox = {
409
+ mode: "inherit",
410
+ sandboxId: parentSandboxId,
411
+ ...context.sandboxStateUpdate && { stateUpdate: context.sandboxStateUpdate }
412
+ };
413
+ } else if (sandboxCfg.source === "own") {
414
+ const prevSbId = continuationThreadId ? threadSandboxes.get(continuationThreadId) : void 0;
415
+ if (prevSbId) {
416
+ sandbox = { mode: "fork", sandboxId: prevSbId };
417
+ }
418
+ }
366
419
  const workflowInput = {
367
- ...args.threadId && args.threadId !== null && config.allowThreadContinuation && {
368
- previousThreadId: args.threadId
369
- },
370
- ...inheritSandbox && { sandboxId: parentSandboxId }
420
+ ...thread && { thread },
421
+ ...sandbox && { sandbox },
422
+ ...sandboxCfg.shutdown && { sandboxShutdown: sandboxCfg.shutdown }
371
423
  };
372
424
  const resolvedContext = config.context === void 0 ? void 0 : typeof config.context === "function" ? config.context() : config.context;
373
425
  const childOpts = {
@@ -375,17 +427,44 @@ function createSubagentHandler(subagents) {
375
427
  args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
376
428
  taskQueue: config.taskQueue ?? parentTaskQueue
377
429
  };
430
+ const childHandle = await startChild(config.workflow, childOpts);
431
+ const usesOwnSandbox = sandboxCfg.source === "own" || allowsContinuation && sandboxCfg.source !== "inherit";
432
+ if (usesOwnSandbox) {
433
+ pendingDestroys.set(childWorkflowId, childHandle);
434
+ }
435
+ await Promise.race([
436
+ condition(() => childResults.has(childWorkflowId)),
437
+ childHandle.result()
438
+ ]);
439
+ if (!childResults.has(childWorkflowId)) {
440
+ await condition(() => childResults.has(childWorkflowId));
441
+ }
442
+ const childResult = childResults.get(childWorkflowId);
443
+ childResults.delete(childWorkflowId);
444
+ if (!childResult) {
445
+ return {
446
+ toolResponse: "Subagent workflow did not signal a result",
447
+ data: null
448
+ };
449
+ }
378
450
  const {
379
451
  toolResponse,
380
452
  data,
381
453
  usage,
382
- threadId: childThreadId
383
- } = typeof config.workflow === "string" ? await executeChild(config.workflow, childOpts) : await executeChild(config.workflow, childOpts);
454
+ threadId: childThreadId,
455
+ sandboxId: childSandboxId,
456
+ metadata
457
+ } = childResult;
458
+ if (allowsContinuation && childSandboxId && childThreadId) {
459
+ threadSandboxes.set(childThreadId, childSandboxId);
460
+ }
384
461
  if (!toolResponse) {
385
462
  return {
386
463
  toolResponse: "Subagent workflow returned no response",
387
464
  data: null,
388
- ...usage && { usage }
465
+ ...usage && { usage },
466
+ ...childSandboxId && { sandboxId: childSandboxId },
467
+ ...metadata && { metadata }
389
468
  };
390
469
  }
391
470
  const validated = config.resultSchema ? config.resultSchema.safeParse(data) : null;
@@ -393,11 +472,13 @@ function createSubagentHandler(subagents) {
393
472
  return {
394
473
  toolResponse: `Subagent workflow returned invalid data: ${validated.error.message}`,
395
474
  data: null,
396
- ...usage && { usage }
475
+ ...usage && { usage },
476
+ ...childSandboxId && { sandboxId: childSandboxId },
477
+ ...metadata && { metadata }
397
478
  };
398
479
  }
399
480
  let finalToolResponse = toolResponse;
400
- if (config.allowThreadContinuation && childThreadId) {
481
+ if (allowsContinuation && childThreadId) {
401
482
  finalToolResponse = typeof toolResponse === "string" ? `${toolResponse}
402
483
 
403
484
  [${config.agentName} Thread ID: ${childThreadId}]` : toolResponse;
@@ -405,9 +486,22 @@ function createSubagentHandler(subagents) {
405
486
  return {
406
487
  toolResponse: finalToolResponse,
407
488
  data: validated ? validated.data : data,
408
- ...usage && { usage }
489
+ ...usage && { usage },
490
+ ...childSandboxId && { sandboxId: childSandboxId },
491
+ ...metadata && { metadata }
409
492
  };
410
493
  };
494
+ const destroySubagentSandboxes = async () => {
495
+ const handles = [...pendingDestroys.values()];
496
+ pendingDestroys.clear();
497
+ await Promise.all(
498
+ handles.map(async (handle) => {
499
+ await handle.signal(destroySandboxSignal);
500
+ await handle.result();
501
+ })
502
+ );
503
+ };
504
+ return { handler, destroySubagentSandboxes };
411
505
  }
412
506
 
413
507
  // src/lib/subagent/register.ts
@@ -421,12 +515,13 @@ function buildSubagentRegistration(subagents) {
421
515
  if (s.hooks) subagentHooksMap.set(s.agentName, s.hooks);
422
516
  }
423
517
  const resolveSubagentName = (args) => args.subagent;
424
- return {
518
+ const { handler, destroySubagentSandboxes } = createSubagentHandler(subagents);
519
+ const registration = {
425
520
  name: SUBAGENT_TOOL_NAME,
426
521
  enabled: () => getEnabled().length > 0,
427
522
  description: () => createSubagentTool(getEnabled()).description,
428
523
  schema: () => createSubagentTool(getEnabled()).schema,
429
- handler: createSubagentHandler(subagents),
524
+ handler,
430
525
  ...subagentHooksMap.size > 0 && {
431
526
  hooks: {
432
527
  onPreToolUse: async (ctx) => {
@@ -444,6 +539,7 @@ function buildSubagentRegistration(subagents) {
444
539
  }
445
540
  }
446
541
  };
542
+ return { registration, destroySubagentSandboxes };
447
543
  }
448
544
  var READ_SKILL_TOOL_NAME = "ReadSkill";
449
545
  function buildReadSkillDescription(skills) {
@@ -469,6 +565,29 @@ function createReadSkillTool(skills) {
469
565
  }
470
566
 
471
567
  // src/lib/skills/handler.ts
568
+ function formatSkillResponse(skill) {
569
+ const parts = [];
570
+ parts.push(`<skill_content name="${skill.name}">`);
571
+ parts.push(skill.instructions);
572
+ if (skill.location) {
573
+ parts.push(`
574
+ Skill directory: ${skill.location}`);
575
+ parts.push(
576
+ "Relative paths in this skill resolve against the skill directory above."
577
+ );
578
+ }
579
+ const resources = skill.resourceContents ? Object.keys(skill.resourceContents) : [];
580
+ if (resources.length > 0) {
581
+ parts.push("");
582
+ parts.push("<skill_resources>");
583
+ for (const r of resources) {
584
+ parts.push(` <file>${r}</file>`);
585
+ }
586
+ parts.push("</skill_resources>");
587
+ }
588
+ parts.push("</skill_content>");
589
+ return parts.join("\n");
590
+ }
472
591
  function createReadSkillHandler(skills) {
473
592
  const skillMap = new Map(skills.map((s) => [s.name, s]));
474
593
  return (args) => {
@@ -482,22 +601,42 @@ function createReadSkillHandler(skills) {
482
601
  };
483
602
  }
484
603
  return {
485
- toolResponse: skill.instructions,
604
+ toolResponse: formatSkillResponse(skill),
486
605
  data: null
487
606
  };
488
607
  };
489
608
  }
490
609
 
491
610
  // src/lib/skills/register.ts
611
+ function validateSkillNames(skills) {
612
+ const names = skills.map((s) => s.name);
613
+ const dupes = names.filter((n, i) => names.indexOf(n) !== i);
614
+ if (dupes.length > 0) {
615
+ throw new Error(
616
+ `Duplicate skill names: ${[...new Set(dupes)].join(", ")}`
617
+ );
618
+ }
619
+ }
492
620
  function buildSkillRegistration(skills) {
493
621
  if (skills.length === 0) return null;
622
+ validateSkillNames(skills);
494
623
  return {
495
624
  ...createReadSkillTool(skills),
496
625
  handler: createReadSkillHandler(skills)
497
626
  };
498
627
  }
499
- var createSession = async ({
500
- threadId: providedThreadId,
628
+ function collectSkillFiles(skills) {
629
+ let files;
630
+ for (const skill of skills) {
631
+ if (!skill.resourceContents || !skill.location) continue;
632
+ for (const [relPath, content] of Object.entries(skill.resourceContents)) {
633
+ files ??= {};
634
+ files[`${skill.location}/${relPath}`] = content;
635
+ }
636
+ }
637
+ return files;
638
+ }
639
+ async function createSession({
501
640
  agentName,
502
641
  maxTurns = 50,
503
642
  metadata = {},
@@ -510,13 +649,27 @@ var createSession = async ({
510
649
  processToolsInParallel = true,
511
650
  hooks = {},
512
651
  appendSystemPrompt = true,
513
- continueThread = false,
514
652
  waitForInputTimeout = "48h",
515
- sandbox: sandboxOps,
516
- sandboxId: inheritedSandboxId
517
- }) => {
518
- const sourceThreadId = continueThread ? providedThreadId : void 0;
519
- const threadId = continueThread && providedThreadId ? getShortId() : providedThreadId ?? getShortId();
653
+ sandboxOps,
654
+ thread: threadInit,
655
+ sandbox: sandboxInit,
656
+ sandboxShutdown = "destroy"
657
+ }) {
658
+ const threadMode = threadInit?.mode ?? "new";
659
+ let threadId;
660
+ let sourceThreadId;
661
+ switch (threadMode) {
662
+ case "new":
663
+ threadId = threadInit?.mode === "new" && threadInit.threadId ? threadInit.threadId : getShortId();
664
+ break;
665
+ case "continue":
666
+ threadId = threadInit.threadId;
667
+ break;
668
+ case "fork":
669
+ sourceThreadId = threadInit.threadId;
670
+ threadId = getShortId();
671
+ break;
672
+ }
520
673
  const {
521
674
  appendToolResult,
522
675
  appendHumanMessage,
@@ -525,9 +678,13 @@ var createSession = async ({
525
678
  forkThread
526
679
  } = threadOps;
527
680
  const plugins = [];
681
+ let destroySubagentSandboxes;
528
682
  if (subagents) {
529
- const reg = buildSubagentRegistration(subagents);
530
- if (reg) plugins.push(reg);
683
+ const result = buildSubagentRegistration(subagents);
684
+ if (result) {
685
+ plugins.push(result.registration);
686
+ destroySubagentSandboxes = result.destroySubagentSandboxes;
687
+ }
531
688
  }
532
689
  if (skills) {
533
690
  const reg = buildSkillRegistration(skills);
@@ -575,12 +732,52 @@ var createSession = async ({
575
732
  stateManager.run();
576
733
  }
577
734
  );
578
- let sandboxId = inheritedSandboxId;
579
- const ownsSandbox = !sandboxId && !!sandboxOps;
580
- if (ownsSandbox) {
581
- const result = await sandboxOps.createSandbox({ id: threadId });
735
+ const sandboxMode = sandboxInit?.mode;
736
+ let sandboxId;
737
+ let sandboxOwned = false;
738
+ let sandboxStateUpdate;
739
+ if (sandboxMode === "inherit") {
740
+ const inheritInit = sandboxInit;
741
+ sandboxId = inheritInit.sandboxId;
742
+ if (inheritInit.stateUpdate) {
743
+ sandboxStateUpdate = inheritInit.stateUpdate;
744
+ stateManager.mergeUpdate(inheritInit.stateUpdate);
745
+ }
746
+ if (!sandboxOps) {
747
+ throw ApplicationFailure.create({
748
+ message: "sandboxId provided but no sandboxOps \u2014 cannot manage sandbox lifecycle",
749
+ nonRetryable: true
750
+ });
751
+ }
752
+ } else if (sandboxMode === "continue") {
753
+ if (!sandboxOps) {
754
+ throw ApplicationFailure.create({
755
+ message: "No sandboxOps provided \u2014 cannot continue sandbox",
756
+ nonRetryable: true
757
+ });
758
+ }
759
+ sandboxId = sandboxInit.sandboxId;
760
+ sandboxOwned = true;
761
+ } else if (sandboxMode === "fork") {
762
+ if (!sandboxOps) {
763
+ throw ApplicationFailure.create({
764
+ message: "No sandboxOps provided \u2014 cannot fork sandbox",
765
+ nonRetryable: true
766
+ });
767
+ }
768
+ sandboxId = await sandboxOps.forkSandbox(
769
+ sandboxInit.sandboxId
770
+ );
771
+ sandboxOwned = true;
772
+ } else if (sandboxOps) {
773
+ const skillFiles = skills ? collectSkillFiles(skills) : void 0;
774
+ const result = await sandboxOps.createSandbox(
775
+ skillFiles ? { initialFiles: skillFiles } : void 0
776
+ );
582
777
  sandboxId = result.sandboxId;
778
+ sandboxOwned = true;
583
779
  if (result.stateUpdate) {
780
+ sandboxStateUpdate = result.stateUpdate;
584
781
  stateManager.mergeUpdate(result.stateUpdate);
585
782
  }
586
783
  }
@@ -592,9 +789,9 @@ var createSession = async ({
592
789
  });
593
790
  }
594
791
  const systemPrompt = stateManager.getSystemPrompt();
595
- if (continueThread && sourceThreadId) {
792
+ if (threadMode === "fork" && sourceThreadId) {
596
793
  await forkThread(sourceThreadId, threadId);
597
- } else {
794
+ } else if (threadMode === "continue") ; else {
598
795
  if (appendSystemPrompt) {
599
796
  if (!systemPrompt || systemPrompt.trim() === "") {
600
797
  throw ApplicationFailure.create({
@@ -629,7 +826,8 @@ var createSession = async ({
629
826
  threadId,
630
827
  finalMessage: message,
631
828
  exitReason,
632
- usage: stateManager.getTotalUsage()
829
+ usage: stateManager.getTotalUsage(),
830
+ sandboxId
633
831
  };
634
832
  }
635
833
  const parsedToolCalls = [];
@@ -651,7 +849,8 @@ var createSession = async ({
651
849
  parsedToolCalls,
652
850
  {
653
851
  turn: currentTurn,
654
- ...sandboxId !== void 0 && { sandboxId }
852
+ ...sandboxId !== void 0 && { sandboxId },
853
+ ...sandboxStateUpdate && { sandboxStateUpdate }
655
854
  }
656
855
  );
657
856
  for (const result of toolCallResults) {
@@ -680,30 +879,40 @@ var createSession = async ({
680
879
  throw ApplicationFailure.fromError(error);
681
880
  } finally {
682
881
  await callSessionEnd(exitReason, stateManager.getTurns());
683
- if (ownsSandbox && sandboxId && sandboxOps) {
684
- await sandboxOps.destroySandbox(sandboxId);
882
+ if (sandboxOwned && sandboxId && sandboxOps) {
883
+ switch (sandboxShutdown) {
884
+ case "destroy":
885
+ await sandboxOps.destroySandbox(sandboxId);
886
+ break;
887
+ case "pause":
888
+ case "pause-until-parent-close":
889
+ await sandboxOps.pauseSandbox(sandboxId);
890
+ break;
891
+ }
892
+ }
893
+ if (destroySubagentSandboxes) {
894
+ await destroySubagentSandboxes();
685
895
  }
686
896
  }
687
897
  return {
688
898
  threadId,
689
899
  finalMessage: null,
690
900
  exitReason,
691
- usage: stateManager.getTotalUsage()
901
+ usage: stateManager.getTotalUsage(),
902
+ sandboxId
692
903
  };
693
904
  }
694
905
  };
695
- };
906
+ }
696
907
 
697
908
  // src/lib/workflow.ts
698
909
  function defineWorkflow(config, fn) {
699
910
  const workflow = async (input, workflowInput = {}) => {
700
911
  const sessionInput = {
701
912
  agentName: config.name,
702
- ...workflowInput.previousThreadId && {
703
- threadId: workflowInput.previousThreadId,
704
- continueThread: true
705
- },
706
- ...workflowInput.sandboxId && { sandboxId: workflowInput.sandboxId }
913
+ sandboxShutdown: config.sandboxShutdown ?? "destroy",
914
+ ...workflowInput.thread && { thread: workflowInput.thread },
915
+ ...workflowInput.sandbox && { sandbox: workflowInput.sandbox }
707
916
  };
708
917
  return fn(input, sessionInput);
709
918
  };
@@ -884,19 +1093,55 @@ function defineSubagent(definition, overrides) {
884
1093
  ...overrides
885
1094
  };
886
1095
  }
887
-
888
- // src/lib/subagent/workflow.ts
889
1096
  function defineSubagentWorkflow(config, fn) {
890
1097
  const workflow = async (prompt, workflowInput, context) => {
1098
+ const effectiveShutdown = workflowInput.sandboxShutdown ?? config.sandboxShutdown ?? "destroy";
891
1099
  const sessionInput = {
892
1100
  agentName: config.name,
893
- ...workflowInput.previousThreadId && {
894
- threadId: workflowInput.previousThreadId,
895
- continueThread: true
896
- },
897
- ...workflowInput.sandboxId && { sandboxId: workflowInput.sandboxId }
1101
+ sandboxShutdown: effectiveShutdown,
1102
+ ...workflowInput.thread && { thread: workflowInput.thread },
1103
+ ...workflowInput.sandbox && { sandbox: workflowInput.sandbox }
898
1104
  };
899
- return fn(prompt, sessionInput, context ?? {});
1105
+ const { destroySandbox, ...result } = await fn(
1106
+ prompt,
1107
+ sessionInput,
1108
+ context ?? {}
1109
+ );
1110
+ if (effectiveShutdown === "pause-until-parent-close") {
1111
+ if (!destroySandbox) {
1112
+ throw ApplicationFailure.create({
1113
+ message: `Subagent "${config.name}" has sandboxShutdown="pause-until-parent-close" but fn did not return a destroySandbox callback`,
1114
+ nonRetryable: true
1115
+ });
1116
+ }
1117
+ if (!result.sandboxId) {
1118
+ throw ApplicationFailure.create({
1119
+ message: `Subagent "${config.name}" has sandboxShutdown="pause-until-parent-close" but fn did not return a sandboxId`,
1120
+ nonRetryable: true
1121
+ });
1122
+ }
1123
+ }
1124
+ const { parent } = workflowInfo();
1125
+ if (!parent) {
1126
+ throw ApplicationFailure.create({
1127
+ message: "Subagent workflow called without a parent workflow",
1128
+ nonRetryable: true
1129
+ });
1130
+ }
1131
+ const parentHandle = getExternalWorkflowHandle(parent.workflowId);
1132
+ await parentHandle.signal(childResultSignal, {
1133
+ childWorkflowId: workflowInfo().workflowId,
1134
+ result
1135
+ });
1136
+ if (destroySandbox) {
1137
+ let destroyRequested = false;
1138
+ setHandler(destroySandboxSignal, () => {
1139
+ destroyRequested = true;
1140
+ });
1141
+ await condition(() => destroyRequested);
1142
+ await destroySandbox();
1143
+ }
1144
+ return result;
900
1145
  };
901
1146
  Object.defineProperty(workflow, "name", { value: config.name });
902
1147
  return Object.assign(workflow, {