zeitlich 0.2.33 → 0.2.34

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 (117) hide show
  1. package/README.md +17 -6
  2. package/dist/{activities-fnX8-vhR.d.cts → activities-JOqPfKP0.d.cts} +2 -2
  3. package/dist/{activities-YBD5BaHh.d.ts → activities-WwMsjRwm.d.ts} +2 -2
  4. package/dist/adapters/sandbox/bedrock/index.cjs +2 -0
  5. package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
  6. package/dist/adapters/sandbox/bedrock/index.d.cts +4 -3
  7. package/dist/adapters/sandbox/bedrock/index.d.ts +4 -3
  8. package/dist/adapters/sandbox/bedrock/index.js +2 -0
  9. package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
  10. package/dist/adapters/sandbox/bedrock/workflow.cjs +1 -0
  11. package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -1
  12. package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
  13. package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
  14. package/dist/adapters/sandbox/bedrock/workflow.js +1 -0
  15. package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
  16. package/dist/adapters/sandbox/daytona/index.cjs +2 -0
  17. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  18. package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
  19. package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
  20. package/dist/adapters/sandbox/daytona/index.js +2 -0
  21. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  22. package/dist/adapters/sandbox/daytona/workflow.cjs +1 -0
  23. package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
  24. package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
  25. package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
  26. package/dist/adapters/sandbox/daytona/workflow.js +1 -0
  27. package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
  28. package/dist/adapters/sandbox/e2b/index.cjs +3 -0
  29. package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
  30. package/dist/adapters/sandbox/e2b/index.d.cts +2 -1
  31. package/dist/adapters/sandbox/e2b/index.d.ts +2 -1
  32. package/dist/adapters/sandbox/e2b/index.js +3 -0
  33. package/dist/adapters/sandbox/e2b/index.js.map +1 -1
  34. package/dist/adapters/sandbox/e2b/workflow.cjs +1 -0
  35. package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
  36. package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
  37. package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
  38. package/dist/adapters/sandbox/e2b/workflow.js +1 -0
  39. package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
  40. package/dist/adapters/sandbox/inmemory/index.cjs +2 -0
  41. package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
  42. package/dist/adapters/sandbox/inmemory/index.d.cts +2 -1
  43. package/dist/adapters/sandbox/inmemory/index.d.ts +2 -1
  44. package/dist/adapters/sandbox/inmemory/index.js +2 -0
  45. package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
  46. package/dist/adapters/sandbox/inmemory/workflow.cjs +1 -0
  47. package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
  48. package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
  49. package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
  50. package/dist/adapters/sandbox/inmemory/workflow.js +1 -0
  51. package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
  52. package/dist/adapters/thread/anthropic/index.d.cts +5 -5
  53. package/dist/adapters/thread/anthropic/index.d.ts +5 -5
  54. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
  55. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
  56. package/dist/adapters/thread/google-genai/index.d.cts +5 -5
  57. package/dist/adapters/thread/google-genai/index.d.ts +5 -5
  58. package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
  59. package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
  60. package/dist/adapters/thread/langchain/index.d.cts +5 -5
  61. package/dist/adapters/thread/langchain/index.d.ts +5 -5
  62. package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
  63. package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
  64. package/dist/index.cjs +114 -30
  65. package/dist/index.cjs.map +1 -1
  66. package/dist/index.d.cts +10 -9
  67. package/dist/index.d.ts +10 -9
  68. package/dist/index.js +114 -30
  69. package/dist/index.js.map +1 -1
  70. package/dist/{proxy-CTCYWjkr.d.cts → proxy-BesT2ioL.d.cts} +1 -1
  71. package/dist/{proxy-Br4unLTC.d.ts → proxy-Bz6wXYW-.d.ts} +1 -1
  72. package/dist/{thread-manager-Cv_BR28i.d.cts → thread-manager-CCVAOK8g.d.cts} +1 -1
  73. package/dist/{thread-manager-CUubPYPH.d.cts → thread-manager-Cf_34H8w.d.cts} +1 -1
  74. package/dist/{thread-manager-YJLoc1vH.d.ts → thread-manager-ClKAQx78.d.ts} +1 -1
  75. package/dist/{thread-manager-DKWxHUzD.d.ts → thread-manager-DarJIK_b.d.ts} +1 -1
  76. package/dist/{types-Bpq5fDI5.d.cts → types-BGLW5Zyj.d.ts} +35 -20
  77. package/dist/{types-BxiT8w9d.d.ts → types-BVUmLYpj.d.ts} +1 -1
  78. package/dist/{types-DUvEZSDe.d.cts → types-CBH54cwr.d.cts} +1 -1
  79. package/dist/{types-NJDyMyUx.d.cts → types-DPAZ3KCs.d.cts} +1 -1
  80. package/dist/{types-CheCTLeV.d.ts → types-DlLajQcu.d.cts} +35 -20
  81. package/dist/{types-AujBIMMn.d.cts → types-DxCpFNv_.d.cts} +4 -0
  82. package/dist/{types-AujBIMMn.d.ts → types-DxCpFNv_.d.ts} +4 -0
  83. package/dist/{types-DBk-C8zM.d.ts → types-wiGLvxWf.d.ts} +1 -1
  84. package/dist/{workflow-D9nNERvs.d.ts → workflow-_ZGcacCK.d.ts} +3 -3
  85. package/dist/{workflow-Od9vx5Jk.d.cts → workflow-hocXpLwg.d.cts} +3 -3
  86. package/dist/workflow.cjs +108 -30
  87. package/dist/workflow.cjs.map +1 -1
  88. package/dist/workflow.d.cts +3 -3
  89. package/dist/workflow.d.ts +3 -3
  90. package/dist/workflow.js +108 -30
  91. package/dist/workflow.js.map +1 -1
  92. package/package.json +1 -1
  93. package/src/adapters/sandbox/bedrock/index.ts +4 -0
  94. package/src/adapters/sandbox/bedrock/proxy.ts +1 -0
  95. package/src/adapters/sandbox/daytona/index.ts +4 -0
  96. package/src/adapters/sandbox/daytona/proxy.ts +1 -0
  97. package/src/adapters/sandbox/e2b/index.ts +4 -0
  98. package/src/adapters/sandbox/e2b/proxy.ts +1 -0
  99. package/src/adapters/sandbox/inmemory/index.ts +4 -0
  100. package/src/adapters/sandbox/inmemory/proxy.ts +1 -0
  101. package/src/lib/lifecycle.ts +7 -3
  102. package/src/lib/sandbox/manager.ts +7 -0
  103. package/src/lib/sandbox/types.ts +4 -0
  104. package/src/lib/session/session-edge-cases.integration.test.ts +194 -0
  105. package/src/lib/session/session.integration.test.ts +5 -0
  106. package/src/lib/session/session.ts +9 -0
  107. package/src/lib/session/types.ts +5 -0
  108. package/src/lib/subagent/define.ts +1 -1
  109. package/src/lib/subagent/handler.ts +142 -32
  110. package/src/lib/subagent/index.ts +5 -1
  111. package/src/lib/subagent/signals.ts +8 -1
  112. package/src/lib/subagent/subagent.integration.test.ts +532 -25
  113. package/src/lib/subagent/types.ts +32 -15
  114. package/src/lib/subagent/workflow.ts +26 -13
  115. package/src/lib/virtual-fs/manager.ts +1 -1
  116. package/src/lib/virtual-fs/types.ts +2 -2
  117. package/src/lib/virtual-fs/virtual-fs.test.ts +2 -2
@@ -22,19 +22,40 @@ import type {
22
22
  SandboxInit,
23
23
  SubagentSandboxShutdown,
24
24
  } from "../lifecycle";
25
- import { childResultSignal, destroySandboxSignal } from "./signals";
25
+ import {
26
+ childResultSignal,
27
+ childSandboxReadySignal,
28
+ destroySandboxSignal,
29
+ } from "./signals";
26
30
 
27
- /**
28
- * Resolve the shorthand/object `SubagentSandboxConfig` into a normalized form.
29
- */
30
- function resolveSandboxConfig(config?: SubagentSandboxConfig): {
31
+ /** Normalized sandbox config after resolving the union. */
32
+ interface ResolvedSandboxConfig {
31
33
  source: "none" | "inherit" | "own";
34
+ init: "per-call" | "once";
35
+ continuation: "continue" | "fork";
32
36
  shutdown?: SubagentSandboxShutdown;
33
- } {
34
- if (!config || config === "none") return { source: "none" };
35
- if (config === "inherit") return { source: "inherit" };
36
- if (config === "own") return { source: "own" };
37
- return { source: "own", shutdown: config.shutdown };
37
+ }
38
+
39
+ function resolveSandboxConfig(
40
+ config?: SubagentSandboxConfig
41
+ ): ResolvedSandboxConfig {
42
+ if (!config || config === "none") {
43
+ return { source: "none", init: "per-call", continuation: "fork" };
44
+ }
45
+ if (config.source === "inherit") {
46
+ return {
47
+ source: "inherit",
48
+ init: "per-call",
49
+ continuation: config.continuation,
50
+ shutdown: config.shutdown,
51
+ };
52
+ }
53
+ return {
54
+ source: "own",
55
+ init: config.init ?? "per-call",
56
+ continuation: config.continuation,
57
+ shutdown: config.shutdown,
58
+ };
38
59
  }
39
60
 
40
61
  /**
@@ -65,13 +86,30 @@ export function createSubagentHandler<
65
86
  string,
66
87
  Awaited<ReturnType<typeof startChild>>
67
88
  >();
68
- /** Maps childThreadId → sandboxId for sandbox continuation across invocations */
89
+ /** Maps childThreadId → sandboxId for sandbox continuation across invocations (init: per-call) */
69
90
  const threadSandboxes = new Map<string, string>();
91
+ /** Maps agentName → sandboxId for persistent sandboxes (init: once) */
92
+ const persistentSandboxes = new Map<string, string>();
93
+ /** Tracks agents whose first lazy sandbox creation is in-flight (guards concurrent init) */
94
+ const persistentSandboxCreating = new Set<string>();
95
+ /** Reverse lookup: childWorkflowId → agentName for in-flight lazy creators */
96
+ const lazyCreatorAgent = new Map<string, string>();
70
97
 
71
98
  setHandler(childResultSignal, ({ childWorkflowId, result }) => {
72
99
  childResults.set(childWorkflowId, result);
73
100
  });
74
101
 
102
+ setHandler(
103
+ childSandboxReadySignal,
104
+ ({ childWorkflowId, sandboxId }) => {
105
+ const agentName = lazyCreatorAgent.get(childWorkflowId);
106
+ if (agentName && !persistentSandboxes.has(agentName)) {
107
+ persistentSandboxes.set(agentName, sandboxId);
108
+ lazyCreatorAgent.delete(childWorkflowId);
109
+ }
110
+ }
111
+ );
112
+
75
113
  const handler = async (
76
114
  args: SubagentArgs,
77
115
  context: RouterContext
@@ -111,25 +149,74 @@ export function createSubagentHandler<
111
149
 
112
150
  // --- Build sandbox init ---
113
151
  let sandbox: SandboxInit | undefined;
152
+ let sandboxShutdownOverride: SubagentSandboxShutdown | undefined;
153
+ let isLazyCreator = false;
154
+
114
155
  if (sandboxCfg.source === "inherit" && parentSandboxId) {
115
- sandbox = {
116
- mode: "inherit",
117
- sandboxId: parentSandboxId,
118
- };
156
+ if (sandboxCfg.continuation === "fork") {
157
+ sandbox = { mode: "fork", sandboxId: parentSandboxId };
158
+ } else {
159
+ sandbox = { mode: "inherit", sandboxId: parentSandboxId };
160
+ }
119
161
  } else if (sandboxCfg.source === "own") {
120
- const prevSbId = continuationThreadId
121
- ? threadSandboxes.get(continuationThreadId)
122
- : undefined;
123
- if (prevSbId) {
124
- sandbox = { mode: "fork", sandboxId: prevSbId };
162
+ const isLazy = sandboxCfg.init === "once";
163
+
164
+ let baseSandboxId: string | undefined;
165
+ if (isLazy) {
166
+ baseSandboxId = persistentSandboxes.get(config.agentName);
167
+ if (!baseSandboxId) {
168
+ if (persistentSandboxCreating.has(config.agentName)) {
169
+ // Another call is already creating — wait for it to finish
170
+ await condition(() => persistentSandboxes.has(config.agentName));
171
+ baseSandboxId = persistentSandboxes.get(config.agentName);
172
+ } else {
173
+ // We're the first concurrent caller — claim the creator role
174
+ persistentSandboxCreating.add(config.agentName);
175
+ isLazyCreator = true;
176
+ }
177
+ }
178
+ } else if (continuationThreadId) {
179
+ baseSandboxId = threadSandboxes.get(continuationThreadId);
180
+ }
181
+
182
+ if (baseSandboxId) {
183
+ sandbox = {
184
+ mode: sandboxCfg.continuation === "continue" ? "continue" : "fork",
185
+ sandboxId: baseSandboxId,
186
+ };
187
+ }
188
+
189
+ // Ensure the sandbox survives for future continuation/fork:
190
+ // - first lazy call (creator): pause-until-parent-close so parent can clean up
191
+ // - continuation=continue: sandbox must survive for next call
192
+ // - lazy+fork (non-creator): template must survive for future forks
193
+ //
194
+ // Skip the override when the user already configured a *-until-parent-close
195
+ // shutdown — that already guarantees survival.
196
+ const userShutdown = sandboxCfg.shutdown;
197
+ const alreadySurvives =
198
+ userShutdown === "pause-until-parent-close" ||
199
+ userShutdown === "keep-until-parent-close" ||
200
+ userShutdown === "pause" ||
201
+ userShutdown === "keep";
202
+
203
+ const mustSurvive =
204
+ isLazyCreator ||
205
+ sandboxCfg.continuation === "continue" ||
206
+ (isLazy && sandboxCfg.continuation === "fork");
207
+
208
+ if (mustSurvive && !alreadySurvives) {
209
+ sandboxShutdownOverride = isLazyCreator
210
+ ? "pause-until-parent-close"
211
+ : "pause";
125
212
  }
126
- // When no previous sandbox, omit — the child will create its own via sandboxOps
127
213
  }
128
214
 
129
215
  const workflowInput: SubagentWorkflowInput = {
130
216
  ...(thread && { thread }),
131
217
  ...(sandbox && { sandbox }),
132
- ...(sandboxCfg.shutdown && { sandboxShutdown: sandboxCfg.shutdown }),
218
+ sandboxShutdown:
219
+ sandboxShutdownOverride ?? sandboxCfg.shutdown ?? undefined,
133
220
  };
134
221
 
135
222
  const resolvedContext =
@@ -148,6 +235,10 @@ export function createSubagentHandler<
148
235
  taskQueue: config.taskQueue ?? parentTaskQueue,
149
236
  };
150
237
 
238
+ if (isLazyCreator) {
239
+ lazyCreatorAgent.set(childWorkflowId, config.agentName);
240
+ }
241
+
151
242
  log.info("subagent spawned", {
152
243
  subagent: config.agentName,
153
244
  childWorkflowId,
@@ -157,14 +248,18 @@ export function createSubagentHandler<
157
248
 
158
249
  const childHandle = await startChild(config.workflow, childOpts);
159
250
 
160
- const effectiveShutdown = sandboxCfg.shutdown ?? "destroy";
161
- const shouldDeferDestroy =
162
- effectiveShutdown === "pause-until-parent-close" &&
163
- (sandboxCfg.source === "own" ||
164
- (allowsContinuation && sandboxCfg.source !== "inherit"));
165
-
166
- if (shouldDeferDestroy) {
167
- pendingDestroys.set(childWorkflowId, childHandle);
251
+ // Track child handles that need signaling at parent shutdown.
252
+ const effectiveShutdown =
253
+ sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
254
+
255
+ if (
256
+ effectiveShutdown === "pause-until-parent-close" ||
257
+ effectiveShutdown === "keep-until-parent-close"
258
+ ) {
259
+ const key = isLazyCreator
260
+ ? `persistent:${config.agentName}`
261
+ : childWorkflowId;
262
+ pendingDestroys.set(key, childHandle);
168
263
  }
169
264
 
170
265
  // Wait for signal from child; race with child completion to propagate failures
@@ -205,8 +300,23 @@ export function createSubagentHandler<
205
300
  metadata,
206
301
  } = childResult;
207
302
 
208
- if (allowsContinuation && childSandboxId && childThreadId) {
209
- threadSandboxes.set(childThreadId, childSandboxId);
303
+ // Store sandbox ID for future continuation/fork
304
+ if (childSandboxId) {
305
+ if (
306
+ sandboxCfg.source === "own" &&
307
+ sandboxCfg.init === "once" &&
308
+ !persistentSandboxes.has(config.agentName)
309
+ ) {
310
+ // Fallback: signal may have already set this via childSandboxReadySignal
311
+ persistentSandboxes.set(config.agentName, childSandboxId);
312
+ } else if (allowsContinuation && childThreadId) {
313
+ threadSandboxes.set(childThreadId, childSandboxId);
314
+ }
315
+ }
316
+
317
+ if (isLazyCreator) {
318
+ persistentSandboxCreating.delete(config.agentName);
319
+ lazyCreatorAgent.delete(childWorkflowId);
210
320
  }
211
321
 
212
322
  if (!toolResponse) {
@@ -17,4 +17,8 @@ export { createSubagentHandler } from "./handler";
17
17
  export { defineSubagent } from "./define";
18
18
  export { defineSubagentWorkflow } from "./workflow";
19
19
  export { buildSubagentRegistration } from "./register";
20
- export { childResultSignal, destroySandboxSignal } from "./signals";
20
+ export {
21
+ childResultSignal,
22
+ childSandboxReadySignal,
23
+ destroySandboxSignal,
24
+ } from "./signals";
@@ -1,8 +1,15 @@
1
1
  import { defineSignal } from "@temporalio/workflow";
2
- import type { ChildResultSignalPayload } from "./types";
2
+ import type {
3
+ ChildResultSignalPayload,
4
+ ChildSandboxReadySignalPayload,
5
+ } from "./types";
3
6
 
4
7
  export const childResultSignal =
5
8
  defineSignal<[ChildResultSignalPayload]>("childResult");
6
9
 
10
+ /** Sent by a child workflow as soon as its sandbox is created, before the agent loop starts. */
11
+ export const childSandboxReadySignal =
12
+ defineSignal<[ChildSandboxReadySignalPayload]>("childSandboxReady");
13
+
7
14
  /** Sent by the parent to tell a subagent it may destroy its sandbox. */
8
15
  export const destroySandboxSignal = defineSignal("destroySandbox");