zeitlich 0.2.38 → 0.2.39

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-Bmu7XnaG.d.ts} +4 -6
  3. package/dist/{activities-CDcwkRZs.d.cts → activities-ByBFLvm2.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 +266 -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 +263 -49
  57. package/dist/index.js.map +1 -1
  58. package/dist/{proxy-D_3x7RN4.d.cts → proxy-BAKzNGRq.d.cts} +1 -1
  59. package/dist/{proxy-CUlKSvZS.d.ts → proxy-DO_MXbY4.d.ts} +1 -1
  60. package/dist/{thread-manager-CVu7o2cs.d.ts → thread-manager-CcRXasqs.d.ts} +2 -4
  61. package/dist/{thread-manager-HSwyh28L.d.cts → thread-manager-ClwSaUnj.d.cts} +2 -4
  62. package/dist/{thread-manager-c1gPopAG.d.ts → thread-manager-D-7lp1JK.d.ts} +2 -4
  63. package/dist/{thread-manager-wGi-LqIP.d.cts → thread-manager-Y8Ucf0Tf.d.cts} +2 -4
  64. package/dist/{types-C06FwR96.d.cts → types-Bcbiq8iv.d.cts} +162 -44
  65. package/dist/{types-BH_IRryz.d.ts → types-DpHTX-iO.d.ts} +54 -6
  66. package/dist/{types-DNr31FzL.d.ts → types-Dt8-HBBT.d.ts} +162 -44
  67. package/dist/{types-BaOw4hKI.d.cts → types-hFFi-Zd9.d.cts} +54 -6
  68. package/dist/{workflow-CSCkpwAL.d.ts → workflow-Bmf9EtDW.d.ts} +82 -2
  69. package/dist/{workflow-DuvMZ8Vm.d.cts → workflow-Bx9utBwb.d.cts} +82 -2
  70. package/dist/workflow.cjs +188 -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 +185 -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 +68 -28
  105. package/src/lib/session/types.ts +60 -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
@@ -1,4 +1,4 @@
1
- import { aq as ToolMap, a0 as SessionConfig, aB as ZeitlichSession, _ as SandboxShutdown, ai as ThreadInit, Z as SandboxInit, t as JsonSerializable, B as BaseAgentState, o as AgentStateManager, aD as ToolRouterOptions, at as ToolRouter, R as RouterContext, J as JsonValue, av as ToolWithHandler, v as ParsedToolCallUnion, ar as ToolNames, a8 as SubagentDefinition, ab as SubagentHooks, ac as SubagentSandboxConfig, a7 as SubagentConfig, ad as SubagentSandboxShutdown, ae as SubagentSessionInput, a9 as SubagentFnResult, a5 as SessionStartHook, a1 as SessionEndHook, D as PostToolUseHook, y as PostToolUseFailureHook, a3 as SessionExitReason, aj as TokenUsage, e as RunAgentConfig, A as AgentResponse, F as FileEntryMetadata, ax as VirtualFileTree, j as TreeMutation, r as FileEntry, ay as VirtualFsOps, g as SkillMetadata, h as Skill, b as ToolHandlerResponse, ao as ToolHandler, aA as WorkflowTask, c as ActivityToolHandler } from './types-C06FwR96.cjs';
1
+ import { as as ToolMap, a1 as SessionConfig, aD as ZeitlichSession, $ as SandboxShutdown, ak as ThreadInit, _ as SandboxInit, u as JsonSerializable, B as BaseAgentState, p as AgentStateManager, aF as ToolRouterOptions, av as ToolRouter, R as RouterContext, J as JsonValue, ax as ToolWithHandler, w as ParsedToolCallUnion, at as ToolNames, aa as SubagentDefinition, ad as SubagentHooks, ae as SubagentSandboxConfig, a9 as SubagentConfig, af as SubagentSandboxShutdown, ag as SubagentSessionInput, ab as SubagentFnResult, a6 as SessionStartHook, a2 as SessionEndHook, E as PostToolUseHook, z as PostToolUseFailureHook, a4 as SessionExitReason, al as TokenUsage, f as RunAgentConfig, A as AgentResponse, F as FileEntryMetadata, az as VirtualFileTree, k as TreeMutation, s as FileEntry, aA as VirtualFsOps, h as SkillMetadata, i as Skill, c as ToolHandlerResponse, aq as ToolHandler, aC as WorkflowTask, d as ActivityToolHandler } from './types-Bcbiq8iv.cjs';
2
2
  import { g as SandboxOps } from './types-yx0LzPGn.cjs';
3
3
  import z$1, { z } from 'zod';
4
4
  import { Sinks, proxyActivities } from '@temporalio/workflow';
@@ -97,6 +97,68 @@ declare function defineWorkflow<TInput, TResult>(config: WorkflowConfig, fn: (in
97
97
  */
98
98
  declare function getShortId(length?: number): string;
99
99
 
100
+ /**
101
+ * Public helpers for zeitlich's Redis thread storage layout.
102
+ *
103
+ * These are the exact string-building primitives zeitlich's internal thread
104
+ * manager uses for every adapter. Downstream consumers that need to read a
105
+ * persisted thread (for evaluation, observability, admin tooling, etc.)
106
+ * should use these helpers rather than reconstructing the key layout by
107
+ * hand — the layout is versioned with this module, so upgrading zeitlich
108
+ * keeps the consumer in sync.
109
+ *
110
+ * The layout is adapter-agnostic: every thread adapter stores messages the
111
+ * same way.
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * import {
116
+ * getThreadListKey,
117
+ * getThreadMetaKey,
118
+ * THREAD_TTL_SECONDS,
119
+ * } from 'zeitlich';
120
+ *
121
+ * const listKey = getThreadListKey('messages', threadId);
122
+ * const metaKey = getThreadMetaKey('messages', threadId);
123
+ * const ttl = await redis.ttl(listKey); // <= THREAD_TTL_SECONDS
124
+ * ```
125
+ */
126
+ /**
127
+ * TTL (in seconds) applied to every thread list and thread meta key that
128
+ * zeitlich's {@link createThreadManager} writes. Exposed so downstream
129
+ * consumers can size their Redis retention / query windows to match.
130
+ *
131
+ * Current value: 90 days.
132
+ */
133
+ declare const THREAD_TTL_SECONDS: number;
134
+ /**
135
+ * Build the Redis list key that holds a thread's serialized messages.
136
+ *
137
+ * Mirrors the exact key used internally by zeitlich's thread manager,
138
+ * so a consumer calling `redis.lrange(getThreadListKey(key, id), 0, -1)`
139
+ * sees the same data the writer wrote.
140
+ *
141
+ * @param threadKey - Thread key (defaults to `"messages"` inside the
142
+ * thread manager, but downstream adapters may pass
143
+ * their own value).
144
+ * @param threadId - Thread id as provided to the thread manager.
145
+ */
146
+ declare function getThreadListKey(threadKey: string, threadId: string): string;
147
+ /**
148
+ * Build the Redis key that stores a thread's existence marker / metadata.
149
+ *
150
+ * Zeitlich treats the presence of this key as "thread has been
151
+ * initialized"; append/load/fork/truncate operations fail when it is
152
+ * missing. Consumers can use it as a cheap existence probe without
153
+ * scanning the message list.
154
+ *
155
+ * @param threadKey - Thread key (defaults to `"messages"` inside the
156
+ * thread manager, but downstream adapters may pass
157
+ * their own value).
158
+ * @param threadId - Thread id as provided to the thread manager.
159
+ */
160
+ declare function getThreadMetaKey(threadKey: string, threadId: string): string;
161
+
100
162
  /**
101
163
  * Creates an agent state manager for tracking workflow state.
102
164
  * Automatically registers Temporal query and update handlers for the agent.
@@ -194,6 +256,22 @@ type SubagentArgs = {
194
256
  threadId?: string | null;
195
257
  };
196
258
 
259
+ /**
260
+ * Default `workflowRunTimeout` applied to every subagent child workflow
261
+ * unless overridden via `SubagentConfig.workflowOptions.workflowRunTimeout`.
262
+ *
263
+ * Chosen as a safety bound: Temporal retries failing workflow tasks forever
264
+ * by default, so a child that fails to initialize (e.g. missing workflow
265
+ * export) or is otherwise broken will never reach a terminal state on its
266
+ * own and the parent's `Subagent` tool call would hang indefinitely. A
267
+ * bounded run timeout guarantees the child is eventually terminated and the
268
+ * parent receives a structured `ChildWorkflowFailure` it can surface to the
269
+ * agent. One hour is generous enough for realistic agent sessions while
270
+ * still catching hangs; agents that legitimately need longer should set an
271
+ * explicit `workflowOptions.workflowRunTimeout`.
272
+ */
273
+ declare const DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = "1h";
274
+
197
275
  /**
198
276
  * Creates a `SubagentConfig` from a `SubagentDefinition` (returned by `defineSubagentWorkflow`).
199
277
  * Metadata (name, description, resultSchema) is read from the definition — only configure
@@ -264,6 +342,8 @@ declare function defineSubagent<TResult extends z.ZodType = z.ZodType, TContext
264
342
  * });
265
343
  *
266
344
  * const { finalMessage, threadId } = await session.runSession({ stateManager });
345
+ * // `sandboxId`, `snapshot`, and `baseSnapshot` are auto-forwarded
346
+ * // from the session — no need to thread them through manually.
267
347
  * return { toolResponse: finalMessage ?? "No response", data: null, threadId };
268
348
  * },
269
349
  * );
@@ -775,4 +855,4 @@ declare const createAskUserQuestionHandler: () => ActivityToolHandler<AskUserQue
775
855
  }[];
776
856
  }>;
777
857
 
778
- export { proxyRunAgent as $, type AskUserQuestionArgs as A, type BashArgs as B, createTaskListHandler as C, createTaskUpdateHandler as D, createToolRouter as E, type FileEditArgs as F, type GlobArgs as G, defineSubagent as H, defineSubagentWorkflow as I, defineTool as J, defineWorkflow as K, editTool as L, filesWithMimeType as M, formatVirtualFileTree as N, type ObservabilityHooks as O, getShortId as P, globTool as Q, type ReadSkillArgs as R, type SessionEndedEvent as S, type TaskCreateArgs as T, grepTool as U, hasDirectory as V, type WorkflowConfig as W, hasFileWithMimeType as X, hasNoOtherToolCalls as Y, type ZeitlichObservabilitySinks as Z, parseSkillFile as _, type FileReadArgs as a, proxyVirtualFsOps as a0, readFileTool as a1, taskCreateTool as a2, taskGetTool as a3, taskListTool as a4, taskUpdateTool as a5, writeFileTool as a6, type FileWriteArgs as b, type FileTreeAccessor as c, type GrepArgs as d, type SessionStartedEvent as e, type SubagentArgs as f, type TaskGetArgs as g, type TaskListArgs as h, type TaskUpdateArgs as i, type ToolExecutedEvent as j, type TurnCompletedEvent as k, type WorkflowInput as l, type WorkflowSessionInput as m, applyVirtualTreeMutations as n, askUserQuestionTool as o, bashTool as p, composeHooks as q, createAgentStateManager as r, createAskUserQuestionHandler as s, createBashToolDescription as t, createObservabilityHooks as u, createReadSkillHandler as v, createReadSkillTool as w, createSession as x, createTaskCreateHandler as y, createTaskGetHandler as z };
858
+ export { hasDirectory as $, type AskUserQuestionArgs as A, type BashArgs as B, createTaskGetHandler as C, DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT as D, createTaskListHandler as E, type FileEditArgs as F, type GlobArgs as G, createTaskUpdateHandler as H, createToolRouter as I, defineSubagent as J, defineSubagentWorkflow as K, defineTool as L, defineWorkflow as M, editTool as N, type ObservabilityHooks as O, filesWithMimeType as P, formatVirtualFileTree as Q, type ReadSkillArgs as R, type SessionEndedEvent as S, THREAD_TTL_SECONDS as T, getShortId as U, getThreadListKey as V, type WorkflowConfig as W, getThreadMetaKey as X, globTool as Y, type ZeitlichObservabilitySinks as Z, grepTool as _, type FileReadArgs as a, hasFileWithMimeType as a0, hasNoOtherToolCalls as a1, parseSkillFile as a2, proxyRunAgent as a3, proxyVirtualFsOps as a4, readFileTool as a5, taskCreateTool as a6, taskGetTool as a7, taskListTool as a8, taskUpdateTool as a9, writeFileTool as aa, type FileWriteArgs as b, type FileTreeAccessor as c, type GrepArgs as d, type SessionStartedEvent as e, type SubagentArgs as f, type TaskCreateArgs as g, type TaskGetArgs as h, type TaskListArgs as i, type TaskUpdateArgs as j, type ToolExecutedEvent as k, type TurnCompletedEvent as l, type WorkflowInput as m, type WorkflowSessionInput as n, applyVirtualTreeMutations as o, askUserQuestionTool as p, bashTool as q, composeHooks as r, createAgentStateManager as s, createAskUserQuestionHandler as t, createBashToolDescription as u, createObservabilityHooks as v, createReadSkillHandler as w, createReadSkillTool as x, createSession as y, createTaskCreateHandler as z };
package/dist/workflow.cjs CHANGED
@@ -440,6 +440,7 @@ function createSubagentTool(subagents) {
440
440
  var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
441
441
 
442
442
  // src/lib/subagent/handler.ts
443
+ var DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = "1h";
443
444
  function resolveSandboxConfig(config) {
444
445
  if (!config || config === "none") {
445
446
  return { source: "none", init: "per-call", continuation: "fork" };
@@ -471,17 +472,28 @@ function createSubagentHandler(subagents) {
471
472
  const threadSandboxes = /* @__PURE__ */ new Map();
472
473
  const persistentSandboxes = /* @__PURE__ */ new Map();
473
474
  const persistentSandboxCreating = /* @__PURE__ */ new Set();
475
+ const persistentSandboxCreationError = /* @__PURE__ */ new Map();
474
476
  const lazyCreatorAgent = /* @__PURE__ */ new Map();
477
+ const snapshotBaseCreatorAgent = /* @__PURE__ */ new Map();
475
478
  const threadSnapshots = /* @__PURE__ */ new Map();
476
479
  const persistentBaseSnapshot = /* @__PURE__ */ new Map();
477
480
  const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
478
- workflow.setHandler(childSandboxReadySignal, ({ childWorkflowId, sandboxId }) => {
479
- const agentName = lazyCreatorAgent.get(childWorkflowId);
480
- if (agentName && !persistentSandboxes.has(agentName)) {
481
- persistentSandboxes.set(agentName, sandboxId);
482
- lazyCreatorAgent.delete(childWorkflowId);
481
+ const persistentBaseSnapshotCreationError = /* @__PURE__ */ new Map();
482
+ workflow.setHandler(
483
+ childSandboxReadySignal,
484
+ ({ childWorkflowId, sandboxId, baseSnapshot }) => {
485
+ const lazyAgent = lazyCreatorAgent.get(childWorkflowId);
486
+ if (lazyAgent && !persistentSandboxes.has(lazyAgent)) {
487
+ persistentSandboxes.set(lazyAgent, sandboxId);
488
+ lazyCreatorAgent.delete(childWorkflowId);
489
+ }
490
+ const snapAgent = snapshotBaseCreatorAgent.get(childWorkflowId);
491
+ if (snapAgent && baseSnapshot && !persistentBaseSnapshot.has(snapAgent)) {
492
+ persistentBaseSnapshot.set(snapAgent, baseSnapshot);
493
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
494
+ }
483
495
  }
484
- });
496
+ );
485
497
  const handler = async (args, context) => {
486
498
  const config = subagents.find((s) => s.agentName === args.subagent);
487
499
  if (!config) {
@@ -537,8 +549,20 @@ function createSubagentHandler(subagents) {
537
549
  baseSnap = persistentBaseSnapshot.get(config.agentName);
538
550
  if (!baseSnap) {
539
551
  if (persistentBaseSnapshotCreating.has(config.agentName)) {
540
- await workflow.condition(() => persistentBaseSnapshot.has(config.agentName));
552
+ await workflow.condition(
553
+ () => persistentBaseSnapshot.has(config.agentName) || persistentBaseSnapshotCreationError.has(config.agentName) || !persistentBaseSnapshotCreating.has(config.agentName)
554
+ );
555
+ const creatorErr = persistentBaseSnapshotCreationError.get(
556
+ config.agentName
557
+ );
558
+ if (creatorErr !== void 0) {
559
+ throw creatorErr;
560
+ }
541
561
  baseSnap = persistentBaseSnapshot.get(config.agentName);
562
+ if (!baseSnap) {
563
+ persistentBaseSnapshotCreating.add(config.agentName);
564
+ isSnapshotBaseCreator = true;
565
+ }
542
566
  } else {
543
567
  persistentBaseSnapshotCreating.add(config.agentName);
544
568
  isSnapshotBaseCreator = true;
@@ -556,8 +580,20 @@ function createSubagentHandler(subagents) {
556
580
  baseSandboxId = persistentSandboxes.get(config.agentName);
557
581
  if (!baseSandboxId) {
558
582
  if (persistentSandboxCreating.has(config.agentName)) {
559
- await workflow.condition(() => persistentSandboxes.has(config.agentName));
583
+ await workflow.condition(
584
+ () => persistentSandboxes.has(config.agentName) || persistentSandboxCreationError.has(config.agentName) || !persistentSandboxCreating.has(config.agentName)
585
+ );
586
+ const creatorErr = persistentSandboxCreationError.get(
587
+ config.agentName
588
+ );
589
+ if (creatorErr !== void 0) {
590
+ throw creatorErr;
591
+ }
560
592
  baseSandboxId = persistentSandboxes.get(config.agentName);
593
+ if (!baseSandboxId) {
594
+ persistentSandboxCreating.add(config.agentName);
595
+ isLazyCreator = true;
596
+ }
561
597
  } else {
562
598
  persistentSandboxCreating.add(config.agentName);
563
599
  isLazyCreator = true;
@@ -586,6 +622,12 @@ function createSubagentHandler(subagents) {
586
622
  };
587
623
  const resolvedContext = config.context === void 0 ? void 0 : typeof config.context === "function" ? config.context() : config.context;
588
624
  const childOpts = {
625
+ // Apply a bounded run timeout by default so a child workflow that
626
+ // fails to initialize or otherwise never reaches a terminal state
627
+ // cannot hang the parent's `Subagent` tool call forever. Callers can
628
+ // raise, lower, or disable it via `workflowOptions.workflowRunTimeout`.
629
+ workflowRunTimeout: DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT,
630
+ ...config.workflowOptions ?? {},
589
631
  workflowId: childWorkflowId,
590
632
  args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
591
633
  taskQueue: config.taskQueue ?? parentTaskQueue
@@ -593,13 +635,39 @@ function createSubagentHandler(subagents) {
593
635
  if (isLazyCreator) {
594
636
  lazyCreatorAgent.set(childWorkflowId, config.agentName);
595
637
  }
638
+ if (isSnapshotBaseCreator) {
639
+ snapshotBaseCreatorAgent.set(childWorkflowId, config.agentName);
640
+ }
596
641
  workflow.log.info("subagent spawned", {
597
642
  subagent: config.agentName,
598
643
  childWorkflowId,
599
644
  threadMode,
600
645
  sandboxSource: sandboxCfg.source
601
646
  });
602
- const childResult = await workflow.executeChild(config.workflow, childOpts);
647
+ let childResult;
648
+ try {
649
+ childResult = await workflow.executeChild(
650
+ config.workflow,
651
+ childOpts
652
+ );
653
+ } catch (err) {
654
+ workflow.log.warn("subagent failed", {
655
+ subagent: config.agentName,
656
+ childWorkflowId,
657
+ error: err instanceof Error ? err.message : String(err)
658
+ });
659
+ if (isLazyCreator) {
660
+ persistentSandboxCreating.delete(config.agentName);
661
+ persistentSandboxCreationError.set(config.agentName, err);
662
+ lazyCreatorAgent.delete(childWorkflowId);
663
+ }
664
+ if (isSnapshotBaseCreator) {
665
+ persistentBaseSnapshotCreating.delete(config.agentName);
666
+ persistentBaseSnapshotCreationError.set(config.agentName, err);
667
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
668
+ }
669
+ throw err;
670
+ }
603
671
  const effectiveShutdown = sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
604
672
  workflow.log.info("subagent completed", {
605
673
  subagent: config.agentName,
@@ -643,10 +711,13 @@ function createSubagentHandler(subagents) {
643
711
  }
644
712
  if (isLazyCreator) {
645
713
  persistentSandboxCreating.delete(config.agentName);
714
+ persistentSandboxCreationError.delete(config.agentName);
646
715
  lazyCreatorAgent.delete(childWorkflowId);
647
716
  }
648
717
  if (isSnapshotBaseCreator) {
649
718
  persistentBaseSnapshotCreating.delete(config.agentName);
719
+ persistentBaseSnapshotCreationError.delete(config.agentName);
720
+ snapshotBaseCreatorAgent.delete(childWorkflowId);
650
721
  }
651
722
  if (!toolResponse) {
652
723
  return {
@@ -889,6 +960,7 @@ async function createSession({
889
960
  sandbox: sandboxInit,
890
961
  sandboxShutdown = "destroy",
891
962
  onSandboxReady,
963
+ onSessionExit,
892
964
  virtualFs: virtualFsConfig,
893
965
  virtualFsOps
894
966
  }) {
@@ -914,7 +986,8 @@ async function createSession({
914
986
  appendSystemMessage,
915
987
  appendAgentMessage,
916
988
  forkThread,
917
- truncateThread
989
+ loadThreadState,
990
+ saveThreadState
918
991
  } = threadOps;
919
992
  const plugins = [];
920
993
  let destroySubagentSandboxes;
@@ -1042,7 +1115,10 @@ async function createSession({
1042
1115
  baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
1043
1116
  }
1044
1117
  if (sandboxId && sandboxOwned && onSandboxReady) {
1045
- onSandboxReady(sandboxId);
1118
+ onSandboxReady({
1119
+ sandboxId,
1120
+ ...baseSnapshot && { baseSnapshot }
1121
+ });
1046
1122
  }
1047
1123
  if (virtualFsConfig) {
1048
1124
  if (!virtualFsOps) {
@@ -1085,9 +1161,20 @@ async function createSession({
1085
1161
  });
1086
1162
  const sessionStartMs = Date.now();
1087
1163
  const systemPrompt = stateManager.getSystemPrompt();
1164
+ const rehydrateFromSlice = (slice) => {
1165
+ stateManager.mergeUpdate({
1166
+ tasks: new Map(slice.tasks),
1167
+ ...slice.custom
1168
+ });
1169
+ };
1088
1170
  if (threadMode === "fork" && sourceThreadId) {
1089
1171
  await forkThread(sourceThreadId, threadId, threadKey);
1090
- } else if (threadMode === "continue") ; else {
1172
+ const forkedSlice = await loadThreadState(threadId, threadKey);
1173
+ if (forkedSlice) rehydrateFromSlice(forkedSlice);
1174
+ } else if (threadMode === "continue") {
1175
+ const continuedSlice = await loadThreadState(threadId, threadKey);
1176
+ if (continuedSlice) rehydrateFromSlice(continuedSlice);
1177
+ } else {
1091
1178
  if (appendSystemPrompt) {
1092
1179
  if (systemPrompt == null || typeof systemPrompt === "string" && systemPrompt.trim() === "") {
1093
1180
  throw workflow.ApplicationFailure.create({
@@ -1109,24 +1196,21 @@ async function createSession({
1109
1196
  let exitReason = "completed";
1110
1197
  let finalMessage = null;
1111
1198
  try {
1199
+ let assistantId;
1112
1200
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
1113
1201
  stateManager.incrementTurns();
1114
1202
  const currentTurn = stateManager.getTurns();
1115
1203
  workflow.log.debug("turn started", { agentName, threadId, turn: currentTurn });
1116
1204
  stateManager.setTools(toolRouter.getToolDefinitions());
1117
- const {
1118
- message,
1119
- rawToolCalls,
1120
- usage,
1121
- threadLengthAtCall
1122
- } = await runAgent({
1205
+ assistantId ??= workflow.uuid4();
1206
+ const { message, rawToolCalls, usage } = await runAgent({
1123
1207
  threadId,
1124
1208
  threadKey,
1125
1209
  agentName,
1126
- metadata
1210
+ metadata,
1211
+ assistantMessageId: assistantId
1127
1212
  });
1128
- const preAssistantLength = threadLengthAtCall;
1129
- await appendAgentMessage(threadId, workflow.uuid4(), message, threadKey);
1213
+ await appendAgentMessage(threadId, assistantId, message, threadKey);
1130
1214
  if (usage) {
1131
1215
  stateManager.updateUsage(usage);
1132
1216
  }
@@ -1180,15 +1264,9 @@ async function createSession({
1180
1264
  toolCallId: rewind.toolCallId,
1181
1265
  toolName: rewind.toolName
1182
1266
  });
1183
- if (preAssistantLength === void 0) {
1184
- throw workflow.ApplicationFailure.create({
1185
- message: "Rewind requested but runAgent did not report `threadLengthAtCall`; the adapter must populate it to support rewinds.",
1186
- nonRetryable: true
1187
- });
1188
- }
1189
- await truncateThread(threadId, preAssistantLength, threadKey);
1190
1267
  continue;
1191
1268
  }
1269
+ assistantId = void 0;
1192
1270
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
1193
1271
  const conditionMet = await workflow.condition(
1194
1272
  () => stateManager.getStatus() === "RUNNING",
@@ -1221,6 +1299,19 @@ async function createSession({
1221
1299
  });
1222
1300
  throw workflow.ApplicationFailure.fromError(error);
1223
1301
  } finally {
1302
+ try {
1303
+ await saveThreadState(
1304
+ threadId,
1305
+ stateManager.getPersistedSlice(),
1306
+ threadKey
1307
+ );
1308
+ } catch (persistError) {
1309
+ workflow.log.warn("failed to persist thread state", {
1310
+ agentName,
1311
+ threadId,
1312
+ error: persistError instanceof Error ? persistError.message : String(persistError)
1313
+ });
1314
+ }
1224
1315
  await callSessionEnd(exitReason, stateManager.getTurns());
1225
1316
  if (sandboxOwned && sandboxId && sandboxOps) {
1226
1317
  switch (sandboxShutdown) {
@@ -1257,6 +1348,12 @@ async function createSession({
1257
1348
  ...baseSnapshot && { hasBaseSnapshot: true },
1258
1349
  ...exitSnapshot && { hasExitSnapshot: true }
1259
1350
  });
1351
+ if (onSessionExit) {
1352
+ onSessionExit({
1353
+ ...sandboxId && { sandboxId },
1354
+ ...exitSnapshot && { snapshot: exitSnapshot }
1355
+ });
1356
+ }
1260
1357
  return {
1261
1358
  threadId,
1262
1359
  finalMessage,
@@ -1285,6 +1382,15 @@ function defineWorkflow(config, fn) {
1285
1382
  return workflow;
1286
1383
  }
1287
1384
 
1385
+ // src/lib/thread/keys.ts
1386
+ var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
1387
+ function getThreadListKey(threadKey, threadId) {
1388
+ return `${threadKey}:thread:${threadId}`;
1389
+ }
1390
+ function getThreadMetaKey(threadKey, threadId) {
1391
+ return `${threadKey}:meta:thread:${threadId}`;
1392
+ }
1393
+
1288
1394
  // src/lib/types.ts
1289
1395
  function isTerminalStatus(status) {
1290
1396
  return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
@@ -1304,11 +1410,19 @@ function createAgentStateManager({
1304
1410
  let systemPrompt = initialState?.systemPrompt;
1305
1411
  const tasks = new Map(initialState?.tasks);
1306
1412
  const {
1307
- status: _,
1308
- version: __,
1309
- turns: ___,
1310
- tasks: ____,
1311
- tools: _____,
1413
+ status: _status,
1414
+ version: _version,
1415
+ turns: _turns,
1416
+ tasks: _tasks,
1417
+ tools: _tools,
1418
+ systemPrompt: _systemPrompt,
1419
+ fileTree: _fileTree,
1420
+ inlineFiles: _inlineFiles,
1421
+ virtualFsCtx: _virtualFsCtx,
1422
+ totalInputTokens: _totalInputTokens,
1423
+ totalOutputTokens: _totalOutputTokens,
1424
+ cachedWriteTokens: _cachedWriteTokens,
1425
+ cachedReadTokens: _cachedReadTokens,
1312
1426
  ...custom
1313
1427
  } = initialState ?? {};
1314
1428
  const customState = custom;
@@ -1388,7 +1502,14 @@ function createAgentStateManager({
1388
1502
  version++;
1389
1503
  },
1390
1504
  mergeUpdate(update) {
1391
- Object.assign(customState, update);
1505
+ const { tasks: nextTasks, ...rest } = update;
1506
+ if (nextTasks) {
1507
+ tasks.clear();
1508
+ for (const [id, task] of nextTasks) {
1509
+ tasks.set(id, task);
1510
+ }
1511
+ }
1512
+ Object.assign(customState, rest);
1392
1513
  version++;
1393
1514
  },
1394
1515
  getCurrentState() {
@@ -1426,6 +1547,12 @@ function createAgentStateManager({
1426
1547
  }
1427
1548
  return deleted;
1428
1549
  },
1550
+ getPersistedSlice() {
1551
+ return {
1552
+ tasks: Array.from(tasks.entries()),
1553
+ custom: { ...customState }
1554
+ };
1555
+ },
1429
1556
  updateUsage(usage) {
1430
1557
  totalInputTokens += usage.inputTokens ?? 0;
1431
1558
  totalOutputTokens += usage.outputTokens ?? 0;
@@ -1469,22 +1596,42 @@ function defineSubagentWorkflow(config, fn) {
1469
1596
  });
1470
1597
  }
1471
1598
  const parentHandle = workflow.getExternalWorkflowHandle(parent.workflowId);
1599
+ let capturedSandboxId;
1600
+ let capturedSnapshot;
1601
+ let capturedBaseSnapshot;
1602
+ let capturedThreadId;
1472
1603
  const sessionInput = {
1473
1604
  agentName: config.name,
1474
1605
  sandboxShutdown: effectiveShutdown,
1475
1606
  ...workflowInput.thread && { thread: workflowInput.thread },
1476
1607
  ...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
1477
- onSandboxReady: (sandboxId) => {
1608
+ onSandboxReady: ({ sandboxId, baseSnapshot }) => {
1609
+ capturedBaseSnapshot = baseSnapshot;
1478
1610
  const isReuse = workflowInput.sandbox?.mode === "continue";
1479
1611
  if (!isReuse) {
1480
1612
  void parentHandle.signal(childSandboxReadySignal, {
1481
1613
  childWorkflowId: workflow.workflowInfo().workflowId,
1482
- sandboxId
1614
+ sandboxId,
1615
+ ...baseSnapshot && { baseSnapshot }
1483
1616
  });
1484
1617
  }
1618
+ },
1619
+ onSessionExit: ({ sandboxId, snapshot, threadId }) => {
1620
+ capturedSandboxId = sandboxId;
1621
+ capturedSnapshot = snapshot;
1622
+ capturedThreadId = threadId;
1623
+ }
1624
+ };
1625
+ const result = await fn(prompt, sessionInput, context ?? {});
1626
+ return {
1627
+ ...result,
1628
+ ...capturedThreadId !== void 0 && { threadId: capturedThreadId },
1629
+ ...capturedSandboxId !== void 0 && { sandboxId: capturedSandboxId },
1630
+ ...capturedSnapshot !== void 0 && { snapshot: capturedSnapshot },
1631
+ ...capturedBaseSnapshot !== void 0 && {
1632
+ baseSnapshot: capturedBaseSnapshot
1485
1633
  }
1486
1634
  };
1487
- return fn(prompt, sessionInput, context ?? {});
1488
1635
  };
1489
1636
  Object.defineProperty(workflow$1, "name", { value: config.name });
1490
1637
  return Object.assign(workflow$1, {
@@ -2143,8 +2290,10 @@ var createAskUserQuestionHandler = () => async (args) => {
2143
2290
  };
2144
2291
  };
2145
2292
 
2293
+ exports.DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT;
2146
2294
  exports.SandboxNotFoundError = SandboxNotFoundError;
2147
2295
  exports.SandboxNotSupportedError = SandboxNotSupportedError;
2296
+ exports.THREAD_TTL_SECONDS = THREAD_TTL_SECONDS;
2148
2297
  exports.applyVirtualTreeMutations = applyVirtualTreeMutations;
2149
2298
  exports.askUserQuestionTool = askUserQuestionTool;
2150
2299
  exports.bashTool = bashTool;
@@ -2169,6 +2318,8 @@ exports.editTool = editTool;
2169
2318
  exports.filesWithMimeType = filesWithMimeType;
2170
2319
  exports.formatVirtualFileTree = formatVirtualFileTree;
2171
2320
  exports.getShortId = getShortId;
2321
+ exports.getThreadListKey = getThreadListKey;
2322
+ exports.getThreadMetaKey = getThreadMetaKey;
2172
2323
  exports.globTool = globTool;
2173
2324
  exports.grepTool = grepTool;
2174
2325
  exports.hasDirectory = hasDirectory;