zeitlich 0.2.38 → 0.2.40

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 (125) hide show
  1. package/README.md +18 -0
  2. package/dist/{activities-BKhMtKDd.d.ts → activities-CULxRzJ1.d.ts} +4 -6
  3. package/dist/{activities-CDcwkRZs.d.cts → activities-CvUrG3YG.d.cts} +4 -6
  4. package/dist/adapter-id-BB-mmrts.d.cts +17 -0
  5. package/dist/adapter-id-BB-mmrts.d.ts +17 -0
  6. package/dist/adapter-id-CMwVrVqv.d.cts +17 -0
  7. package/dist/adapter-id-CMwVrVqv.d.ts +17 -0
  8. package/dist/adapter-id-CbY2zeSt.d.cts +17 -0
  9. package/dist/adapter-id-CbY2zeSt.d.ts +17 -0
  10. package/dist/adapters/thread/anthropic/index.cjs +140 -23
  11. package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
  12. package/dist/adapters/thread/anthropic/index.d.cts +8 -7
  13. package/dist/adapters/thread/anthropic/index.d.ts +8 -7
  14. package/dist/adapters/thread/anthropic/index.js +140 -24
  15. package/dist/adapters/thread/anthropic/index.js.map +1 -1
  16. package/dist/adapters/thread/anthropic/workflow.cjs +8 -3
  17. package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
  18. package/dist/adapters/thread/anthropic/workflow.d.cts +5 -4
  19. package/dist/adapters/thread/anthropic/workflow.d.ts +5 -4
  20. package/dist/adapters/thread/anthropic/workflow.js +8 -4
  21. package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
  22. package/dist/adapters/thread/google-genai/index.cjs +140 -23
  23. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  24. package/dist/adapters/thread/google-genai/index.d.cts +5 -4
  25. package/dist/adapters/thread/google-genai/index.d.ts +5 -4
  26. package/dist/adapters/thread/google-genai/index.js +140 -24
  27. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  28. package/dist/adapters/thread/google-genai/workflow.cjs +8 -3
  29. package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
  30. package/dist/adapters/thread/google-genai/workflow.d.cts +5 -4
  31. package/dist/adapters/thread/google-genai/workflow.d.ts +5 -4
  32. package/dist/adapters/thread/google-genai/workflow.js +8 -4
  33. package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
  34. package/dist/adapters/thread/index.cjs +16 -0
  35. package/dist/adapters/thread/index.cjs.map +1 -0
  36. package/dist/adapters/thread/index.d.cts +34 -0
  37. package/dist/adapters/thread/index.d.ts +34 -0
  38. package/dist/adapters/thread/index.js +12 -0
  39. package/dist/adapters/thread/index.js.map +1 -0
  40. package/dist/adapters/thread/langchain/index.cjs +139 -24
  41. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  42. package/dist/adapters/thread/langchain/index.d.cts +8 -7
  43. package/dist/adapters/thread/langchain/index.d.ts +8 -7
  44. package/dist/adapters/thread/langchain/index.js +139 -25
  45. package/dist/adapters/thread/langchain/index.js.map +1 -1
  46. package/dist/adapters/thread/langchain/workflow.cjs +8 -3
  47. package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
  48. package/dist/adapters/thread/langchain/workflow.d.cts +5 -4
  49. package/dist/adapters/thread/langchain/workflow.d.ts +5 -4
  50. package/dist/adapters/thread/langchain/workflow.js +8 -4
  51. package/dist/adapters/thread/langchain/workflow.js.map +1 -1
  52. package/dist/index.cjs +267 -48
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.d.cts +6 -6
  55. package/dist/index.d.ts +6 -6
  56. package/dist/index.js +264 -49
  57. package/dist/index.js.map +1 -1
  58. package/dist/{proxy-D_3x7RN4.d.cts → proxy-5EbwzaY4.d.cts} +1 -1
  59. package/dist/{proxy-CUlKSvZS.d.ts → proxy-wZufFfBh.d.ts} +1 -1
  60. package/dist/{thread-manager-CVu7o2cs.d.ts → thread-manager-BNiIt5r8.d.ts} +2 -4
  61. package/dist/{thread-manager-c1gPopAG.d.ts → thread-manager-BoN5DOvG.d.cts} +2 -4
  62. package/dist/{thread-manager-wGi-LqIP.d.cts → thread-manager-BqBAIsED.d.ts} +2 -4
  63. package/dist/{thread-manager-HSwyh28L.d.cts → thread-manager-DF8WuCRs.d.cts} +2 -4
  64. package/dist/{types-BH_IRryz.d.ts → types-C7OoY7h8.d.ts} +54 -6
  65. package/dist/{types-C06FwR96.d.cts → types-Cn2r3ol3.d.cts} +163 -44
  66. package/dist/{types-BaOw4hKI.d.cts → types-CuISs0Ub.d.cts} +54 -6
  67. package/dist/{types-DNr31FzL.d.ts → types-DeQH84C_.d.ts} +163 -44
  68. package/dist/{workflow-CSCkpwAL.d.ts → workflow-C2MZZj5K.d.ts} +82 -2
  69. package/dist/{workflow-DuvMZ8Vm.d.cts → workflow-DhplIN65.d.cts} +82 -2
  70. package/dist/workflow.cjs +189 -37
  71. package/dist/workflow.cjs.map +1 -1
  72. package/dist/workflow.d.cts +2 -2
  73. package/dist/workflow.d.ts +2 -2
  74. package/dist/workflow.js +186 -38
  75. package/dist/workflow.js.map +1 -1
  76. package/package.json +11 -1
  77. package/src/adapters/thread/adapter-id.test.ts +42 -0
  78. package/src/adapters/thread/anthropic/activities.ts +33 -7
  79. package/src/adapters/thread/anthropic/adapter-id.ts +16 -0
  80. package/src/adapters/thread/anthropic/fork-transform.test.ts +291 -0
  81. package/src/adapters/thread/anthropic/index.ts +3 -0
  82. package/src/adapters/thread/anthropic/model-invoker.ts +8 -4
  83. package/src/adapters/thread/anthropic/proxy.ts +3 -2
  84. package/src/adapters/thread/anthropic/thread-manager.ts +27 -4
  85. package/src/adapters/thread/google-genai/activities.ts +33 -7
  86. package/src/adapters/thread/google-genai/adapter-id.ts +16 -0
  87. package/src/adapters/thread/google-genai/fork-transform.test.ts +149 -0
  88. package/src/adapters/thread/google-genai/index.ts +3 -0
  89. package/src/adapters/thread/google-genai/model-invoker.ts +7 -3
  90. package/src/adapters/thread/google-genai/proxy.ts +3 -2
  91. package/src/adapters/thread/google-genai/thread-manager.ts +27 -4
  92. package/src/adapters/thread/index.ts +39 -0
  93. package/src/adapters/thread/langchain/activities.ts +33 -7
  94. package/src/adapters/thread/langchain/adapter-id.ts +16 -0
  95. package/src/adapters/thread/langchain/fork-transform.test.ts +142 -0
  96. package/src/adapters/thread/langchain/index.ts +3 -0
  97. package/src/adapters/thread/langchain/model-invoker.ts +8 -3
  98. package/src/adapters/thread/langchain/proxy.ts +3 -2
  99. package/src/adapters/thread/langchain/thread-manager.ts +27 -4
  100. package/src/lib/lifecycle.ts +3 -1
  101. package/src/lib/model/types.ts +7 -10
  102. package/src/lib/session/session-edge-cases.integration.test.ts +131 -63
  103. package/src/lib/session/session.integration.test.ts +174 -5
  104. package/src/lib/session/session.ts +69 -28
  105. package/src/lib/session/types.ts +61 -9
  106. package/src/lib/state/index.ts +1 -0
  107. package/src/lib/state/manager.integration.test.ts +109 -0
  108. package/src/lib/state/manager.ts +38 -8
  109. package/src/lib/state/types.ts +25 -0
  110. package/src/lib/subagent/handler.ts +124 -11
  111. package/src/lib/subagent/index.ts +5 -1
  112. package/src/lib/subagent/subagent.integration.test.ts +528 -0
  113. package/src/lib/subagent/types.ts +63 -14
  114. package/src/lib/subagent/workflow.ts +29 -2
  115. package/src/lib/thread/index.ts +5 -0
  116. package/src/lib/thread/keys.test.ts +101 -0
  117. package/src/lib/thread/keys.ts +94 -0
  118. package/src/lib/thread/manager.test.ts +139 -0
  119. package/src/lib/thread/manager.ts +92 -14
  120. package/src/lib/thread/proxy.ts +2 -0
  121. package/src/lib/thread/types.ts +60 -6
  122. package/src/lib/tool-router/types.ts +16 -8
  123. package/src/lib/types.ts +12 -0
  124. package/src/workflow.ts +12 -1
  125. package/tsup.config.ts +1 -0
@@ -12,6 +12,7 @@ import type { JsonValue } from "../state/types";
12
12
  import type {
13
13
  InferSubagentResult,
14
14
  SubagentConfig,
15
+ SubagentFnResult,
15
16
  SubagentSandboxConfig,
16
17
  SubagentWorkflowInput,
17
18
  } from "./types";
@@ -25,6 +26,22 @@ import type {
25
26
  import type { SandboxOps, SandboxSnapshot } from "../sandbox/types";
26
27
  import { childSandboxReadySignal } from "./signals";
27
28
 
29
+ /**
30
+ * Default `workflowRunTimeout` applied to every subagent child workflow
31
+ * unless overridden via `SubagentConfig.workflowOptions.workflowRunTimeout`.
32
+ *
33
+ * Chosen as a safety bound: Temporal retries failing workflow tasks forever
34
+ * by default, so a child that fails to initialize (e.g. missing workflow
35
+ * export) or is otherwise broken will never reach a terminal state on its
36
+ * own and the parent's `Subagent` tool call would hang indefinitely. A
37
+ * bounded run timeout guarantees the child is eventually terminated and the
38
+ * parent receives a structured `ChildWorkflowFailure` it can surface to the
39
+ * agent. One hour is generous enough for realistic agent sessions while
40
+ * still catching hangs; agents that legitimately need longer should set an
41
+ * explicit `workflowOptions.workflowRunTimeout`.
42
+ */
43
+ export const DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = "1h";
44
+
28
45
  /** Normalized sandbox config after resolving the union. */
29
46
  interface ResolvedSandboxConfig {
30
47
  source: "none" | "inherit" | "own";
@@ -103,8 +120,18 @@ export function createSubagentHandler<
103
120
  const persistentSandboxes = new Map<string, string>();
104
121
  /** Tracks agents whose first lazy sandbox creation is in-flight (guards concurrent init) */
105
122
  const persistentSandboxCreating = new Set<string>();
123
+ /**
124
+ * Latest failure from a lazy-creator call, keyed by agentName. Populated
125
+ * when a creator's `executeChild` throws, consumed by concurrent waiters so
126
+ * they fail deterministically instead of hanging on a condition predicate
127
+ * that will never be satisfied. Cleared once observed by all current
128
+ * waiters (on the next successful creator attempt).
129
+ */
130
+ const persistentSandboxCreationError = new Map<string, unknown>();
106
131
  /** Reverse lookup: childWorkflowId → agentName for in-flight lazy creators */
107
132
  const lazyCreatorAgent = new Map<string, string>();
133
+ /** Reverse lookup: childWorkflowId → agentName for in-flight snapshot-base creators */
134
+ const snapshotBaseCreatorAgent = new Map<string, string>();
108
135
  /** Maps childThreadId → latest snapshot for sandbox continuation via snapshots */
109
136
  const threadSnapshots = new Map<
110
137
  string,
@@ -117,14 +144,24 @@ export function createSubagentHandler<
117
144
  const persistentBaseSnapshot = new Map<string, SandboxSnapshot>();
118
145
  /** Tracks agents whose first snapshot-backed sandbox creation is in-flight */
119
146
  const persistentBaseSnapshotCreating = new Set<string>();
120
-
121
- setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
122
- const agentName = lazyCreatorAgent.get(childWorkflowId);
123
- if (agentName && !persistentSandboxes.has(agentName)) {
124
- persistentSandboxes.set(agentName, sandboxId);
125
- lazyCreatorAgent.delete(childWorkflowId);
147
+ /** Latest failure from a snapshot-base creator call, keyed by agentName. */
148
+ const persistentBaseSnapshotCreationError = new Map<string, unknown>();
149
+
150
+ setHandler(
151
+ childSandboxReadySignal,
152
+ ({ childWorkflowId, sandboxId, baseSnapshot }) => {
153
+ const lazyAgent = lazyCreatorAgent.get(childWorkflowId);
154
+ if (lazyAgent && !persistentSandboxes.has(lazyAgent)) {
155
+ persistentSandboxes.set(lazyAgent, sandboxId);
156
+ lazyCreatorAgent.delete(childWorkflowId);
157
+ }
158
+ const snapAgent = snapshotBaseCreatorAgent.get(childWorkflowId);
159
+ if (snapAgent && baseSnapshot && !persistentBaseSnapshot.has(snapAgent)) {
160
+ persistentBaseSnapshot.set(snapAgent, baseSnapshot);
161
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
162
+ }
126
163
  }
127
- });
164
+ );
128
165
 
129
166
  const handler = async (
130
167
  args: SubagentArgs,
@@ -208,8 +245,23 @@ export function createSubagentHandler<
208
245
  baseSnap = persistentBaseSnapshot.get(config.agentName);
209
246
  if (!baseSnap) {
210
247
  if (persistentBaseSnapshotCreating.has(config.agentName)) {
211
- await condition(() => persistentBaseSnapshot.has(config.agentName));
248
+ await condition(
249
+ () =>
250
+ persistentBaseSnapshot.has(config.agentName) ||
251
+ persistentBaseSnapshotCreationError.has(config.agentName) ||
252
+ !persistentBaseSnapshotCreating.has(config.agentName)
253
+ );
254
+ const creatorErr = persistentBaseSnapshotCreationError.get(
255
+ config.agentName
256
+ );
257
+ if (creatorErr !== undefined) {
258
+ throw creatorErr;
259
+ }
212
260
  baseSnap = persistentBaseSnapshot.get(config.agentName);
261
+ if (!baseSnap) {
262
+ persistentBaseSnapshotCreating.add(config.agentName);
263
+ isSnapshotBaseCreator = true;
264
+ }
213
265
  } else {
214
266
  persistentBaseSnapshotCreating.add(config.agentName);
215
267
  isSnapshotBaseCreator = true;
@@ -229,9 +281,27 @@ export function createSubagentHandler<
229
281
  baseSandboxId = persistentSandboxes.get(config.agentName);
230
282
  if (!baseSandboxId) {
231
283
  if (persistentSandboxCreating.has(config.agentName)) {
232
- // Another call is already creating — wait for it to finish
233
- await condition(() => persistentSandboxes.has(config.agentName));
284
+ // Another call is already creating — wait for it to finish.
285
+ // Also break out if the creator failed, so we can either fail
286
+ // fast with the same error or (if no one has re-claimed the
287
+ // creator role yet) take over ourselves.
288
+ await condition(
289
+ () =>
290
+ persistentSandboxes.has(config.agentName) ||
291
+ persistentSandboxCreationError.has(config.agentName) ||
292
+ !persistentSandboxCreating.has(config.agentName)
293
+ );
294
+ const creatorErr = persistentSandboxCreationError.get(
295
+ config.agentName
296
+ );
297
+ if (creatorErr !== undefined) {
298
+ throw creatorErr;
299
+ }
234
300
  baseSandboxId = persistentSandboxes.get(config.agentName);
301
+ if (!baseSandboxId) {
302
+ persistentSandboxCreating.add(config.agentName);
303
+ isLazyCreator = true;
304
+ }
235
305
  } else {
236
306
  // We're the first concurrent caller — claim the creator role
237
307
  persistentSandboxCreating.add(config.agentName);
@@ -290,6 +360,12 @@ export function createSubagentHandler<
290
360
  : config.context;
291
361
 
292
362
  const childOpts = {
363
+ // Apply a bounded run timeout by default so a child workflow that
364
+ // fails to initialize or otherwise never reaches a terminal state
365
+ // cannot hang the parent's `Subagent` tool call forever. Callers can
366
+ // raise, lower, or disable it via `workflowOptions.workflowRunTimeout`.
367
+ workflowRunTimeout: DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT,
368
+ ...(config.workflowOptions ?? {}),
293
369
  workflowId: childWorkflowId,
294
370
  args:
295
371
  resolvedContext === undefined
@@ -301,6 +377,9 @@ export function createSubagentHandler<
301
377
  if (isLazyCreator) {
302
378
  lazyCreatorAgent.set(childWorkflowId, config.agentName);
303
379
  }
380
+ if (isSnapshotBaseCreator) {
381
+ snapshotBaseCreatorAgent.set(childWorkflowId, config.agentName);
382
+ }
304
383
 
305
384
  log.info("subagent spawned", {
306
385
  subagent: config.agentName,
@@ -309,7 +388,38 @@ export function createSubagentHandler<
309
388
  sandboxSource: sandboxCfg.source,
310
389
  });
311
390
 
312
- const childResult = await executeChild(config.workflow, childOpts);
391
+ // Always clear in-flight creator bookkeeping, even if `executeChild`
392
+ // throws. Otherwise a failing subagent would strand other concurrent
393
+ // callers waiting on `condition(() => persistentSandboxes.has(...))` /
394
+ // `persistentBaseSnapshot.has(...)` forever, because those conditions are
395
+ // only ever satisfied on the success path below. When we were the
396
+ // creator, also publish the error so any already-waiting concurrent
397
+ // callers fail with the same error instead of silently retrying or
398
+ // hanging.
399
+ let childResult: SubagentFnResult<InferSubagentResult<T[number]> | null>;
400
+ try {
401
+ childResult = (await executeChild(
402
+ config.workflow,
403
+ childOpts
404
+ )) as SubagentFnResult<InferSubagentResult<T[number]> | null>;
405
+ } catch (err) {
406
+ log.warn("subagent failed", {
407
+ subagent: config.agentName,
408
+ childWorkflowId,
409
+ error: err instanceof Error ? err.message : String(err),
410
+ });
411
+ if (isLazyCreator) {
412
+ persistentSandboxCreating.delete(config.agentName);
413
+ persistentSandboxCreationError.set(config.agentName, err);
414
+ lazyCreatorAgent.delete(childWorkflowId);
415
+ }
416
+ if (isSnapshotBaseCreator) {
417
+ persistentBaseSnapshotCreating.delete(config.agentName);
418
+ persistentBaseSnapshotCreationError.set(config.agentName, err);
419
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
420
+ }
421
+ throw err;
422
+ }
313
423
 
314
424
  const effectiveShutdown =
315
425
  sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
@@ -386,10 +496,13 @@ export function createSubagentHandler<
386
496
 
387
497
  if (isLazyCreator) {
388
498
  persistentSandboxCreating.delete(config.agentName);
499
+ persistentSandboxCreationError.delete(config.agentName);
389
500
  lazyCreatorAgent.delete(childWorkflowId);
390
501
  }
391
502
  if (isSnapshotBaseCreator) {
392
503
  persistentBaseSnapshotCreating.delete(config.agentName);
504
+ persistentBaseSnapshotCreationError.delete(config.agentName);
505
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
393
506
  }
394
507
 
395
508
  if (!toolResponse) {
@@ -1,4 +1,5 @@
1
1
  export type {
2
+ SubagentChildWorkflowOptions,
2
3
  SubagentConfig,
3
4
  SubagentContext,
4
5
  SubagentDefinition,
@@ -13,7 +14,10 @@ export type {
13
14
  } from "./types";
14
15
  export { createSubagentTool, SUBAGENT_TOOL_NAME } from "./tool";
15
16
  export type { SubagentArgs } from "./tool";
16
- export { createSubagentHandler } from "./handler";
17
+ export {
18
+ createSubagentHandler,
19
+ DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT,
20
+ } from "./handler";
17
21
  export { defineSubagent } from "./define";
18
22
  export { defineSubagentWorkflow } from "./workflow";
19
23
  export { buildSubagentRegistration } from "./register";