zeitlich 0.2.35 → 0.2.37
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.
- package/README.md +146 -92
- package/dist/{activities-BVI2lTwr.d.ts → activities-Bb-nAjwQ.d.ts} +2 -2
- package/dist/{activities-hd4aNnZE.d.cts → activities-vkI4_3CC.d.cts} +2 -2
- package/dist/adapters/sandbox/bedrock/index.cjs +14 -11
- package/dist/adapters/sandbox/bedrock/index.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/index.d.cts +4 -3
- package/dist/adapters/sandbox/bedrock/index.d.ts +4 -3
- package/dist/adapters/sandbox/bedrock/index.js +14 -11
- package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.cjs +2 -0
- package/dist/adapters/sandbox/bedrock/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.d.cts +2 -2
- package/dist/adapters/sandbox/bedrock/workflow.d.ts +2 -2
- package/dist/adapters/sandbox/bedrock/workflow.js +2 -0
- package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
- package/dist/adapters/sandbox/daytona/index.cjs +35 -6
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +3 -1
- package/dist/adapters/sandbox/daytona/index.d.ts +3 -1
- package/dist/adapters/sandbox/daytona/index.js +35 -6
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.cjs +2 -0
- package/dist/adapters/sandbox/daytona/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/daytona/workflow.js +2 -0
- package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
- package/dist/adapters/sandbox/e2b/index.cjs +59 -10
- package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/index.d.cts +5 -3
- package/dist/adapters/sandbox/e2b/index.d.ts +5 -3
- package/dist/adapters/sandbox/e2b/index.js +59 -10
- package/dist/adapters/sandbox/e2b/index.js.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.cjs +2 -0
- package/dist/adapters/sandbox/e2b/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/e2b/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/e2b/workflow.js +2 -0
- package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.cjs +5 -0
- package/dist/adapters/sandbox/inmemory/index.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.d.cts +2 -1
- package/dist/adapters/sandbox/inmemory/index.d.ts +2 -1
- package/dist/adapters/sandbox/inmemory/index.js +5 -0
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.cjs +2 -0
- package/dist/adapters/sandbox/inmemory/workflow.cjs.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.cts +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.d.ts +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.js +2 -0
- package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
- package/dist/adapters/thread/anthropic/index.cjs +71 -36
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +5 -5
- package/dist/adapters/thread/anthropic/index.d.ts +5 -5
- package/dist/adapters/thread/anthropic/index.js +71 -36
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +5 -1
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -5
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
- package/dist/adapters/thread/anthropic/workflow.js +5 -1
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.cjs +50 -25
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +5 -5
- package/dist/adapters/thread/google-genai/index.d.ts +5 -5
- package/dist/adapters/thread/google-genai/index.js +50 -25
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +5 -1
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +5 -5
- package/dist/adapters/thread/google-genai/workflow.d.ts +5 -5
- package/dist/adapters/thread/google-genai/workflow.js +5 -1
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/langchain/index.cjs +34 -7
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +5 -5
- package/dist/adapters/thread/langchain/index.d.ts +5 -5
- package/dist/adapters/thread/langchain/index.js +34 -7
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +5 -1
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
- package/dist/adapters/thread/langchain/workflow.js +5 -1
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/index.cjs +206 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -11
- package/dist/index.d.ts +17 -11
- package/dist/index.js +207 -121
- package/dist/index.js.map +1 -1
- package/dist/{proxy-BjdFGPTm.d.ts → proxy-0smGKvx8.d.ts} +1 -1
- package/dist/{proxy-7RnVaPdJ.d.cts → proxy-DEtowJyd.d.cts} +1 -1
- package/dist/{thread-manager-DjN5JYul.d.ts → thread-manager-3fszQih4.d.ts} +2 -2
- package/dist/{thread-manager-CbpiGq1L.d.ts → thread-manager-C-C4pI2z.d.ts} +2 -2
- package/dist/{thread-manager-BBzNgQWH.d.cts → thread-manager-CzYln2OC.d.cts} +2 -2
- package/dist/{thread-manager-DzXm9eeI.d.cts → thread-manager-D4vgzYrh.d.cts} +2 -2
- package/dist/{types-yiXmqedU.d.ts → types-B37hKoWA.d.ts} +1 -1
- package/dist/{types-DQ1l_gXL.d.cts → types-BO7Yju20.d.cts} +63 -14
- package/dist/{types-wiGLvxWf.d.ts → types-CNuWnvy9.d.ts} +1 -1
- package/dist/{types-CADc5V_P.d.ts → types-CPKDl-y_.d.ts} +63 -14
- package/dist/{types-Mc_4BCfT.d.cts → types-D08CXPh8.d.cts} +1 -1
- package/dist/{types-CBH54cwr.d.cts → types-DWEUmYAJ.d.cts} +1 -1
- package/dist/{types-DxCpFNv_.d.cts → types-tQL9njTu.d.cts} +25 -0
- package/dist/{types-DxCpFNv_.d.ts → types-tQL9njTu.d.ts} +25 -0
- package/dist/{workflow-P2pTSfKu.d.ts → workflow-CjXHbZZc.d.ts} +2 -2
- package/dist/{workflow-DhtWRovz.d.cts → workflow-Do_lzJpT.d.cts} +2 -2
- package/dist/workflow.cjs +182 -114
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +3 -3
- package/dist/workflow.d.ts +3 -3
- package/dist/workflow.js +183 -115
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/sandbox/bedrock/filesystem.ts +6 -12
- package/src/adapters/sandbox/bedrock/index.ts +10 -8
- package/src/adapters/sandbox/bedrock/proxy.ts +2 -0
- package/src/adapters/sandbox/daytona/filesystem.ts +29 -6
- package/src/adapters/sandbox/daytona/index.ts +6 -0
- package/src/adapters/sandbox/daytona/proxy.ts +2 -0
- package/src/adapters/sandbox/e2b/filesystem.ts +5 -4
- package/src/adapters/sandbox/e2b/index.ts +63 -12
- package/src/adapters/sandbox/e2b/proxy.ts +2 -0
- package/src/adapters/sandbox/inmemory/index.ts +5 -0
- package/src/adapters/sandbox/inmemory/proxy.ts +2 -0
- package/src/adapters/thread/anthropic/activities.ts +49 -26
- package/src/adapters/thread/anthropic/model-invoker.ts +15 -6
- package/src/adapters/thread/anthropic/proxy.ts +6 -2
- package/src/adapters/thread/anthropic/thread-manager.test.ts +26 -7
- package/src/adapters/thread/anthropic/thread-manager.ts +60 -46
- package/src/adapters/thread/google-genai/activities.ts +7 -2
- package/src/adapters/thread/google-genai/model-invoker.ts +26 -8
- package/src/adapters/thread/google-genai/proxy.ts +6 -2
- package/src/adapters/thread/google-genai/thread-manager.test.ts +13 -3
- package/src/adapters/thread/google-genai/thread-manager.ts +54 -33
- package/src/adapters/thread/langchain/activities.ts +46 -24
- package/src/adapters/thread/langchain/hooks.test.ts +36 -49
- package/src/adapters/thread/langchain/hooks.ts +18 -5
- package/src/adapters/thread/langchain/model-invoker.ts +3 -3
- package/src/adapters/thread/langchain/proxy.ts +6 -2
- package/src/adapters/thread/langchain/thread-manager.test.ts +5 -1
- package/src/adapters/thread/langchain/thread-manager.ts +20 -9
- package/src/index.ts +4 -1
- package/src/lib/activity.ts +16 -6
- package/src/lib/hooks/types.ts +6 -6
- package/src/lib/lifecycle.ts +9 -1
- package/src/lib/model/proxy.ts +2 -2
- package/src/lib/observability/hooks.ts +4 -5
- package/src/lib/observability/index.ts +1 -4
- package/src/lib/sandbox/manager.ts +21 -4
- package/src/lib/sandbox/node-fs.ts +3 -6
- package/src/lib/sandbox/sandbox.test.ts +36 -3
- package/src/lib/sandbox/tree.integration.test.ts +10 -3
- package/src/lib/sandbox/types.ts +35 -1
- package/src/lib/session/session-edge-cases.integration.test.ts +51 -13
- package/src/lib/session/session.integration.test.ts +139 -0
- package/src/lib/session/session.ts +50 -19
- package/src/lib/session/types.ts +13 -5
- package/src/lib/skills/fs-provider.ts +12 -8
- package/src/lib/skills/handler.ts +1 -1
- package/src/lib/skills/parse.ts +3 -1
- package/src/lib/skills/register.ts +1 -3
- package/src/lib/skills/skills.integration.test.ts +25 -15
- package/src/lib/state/manager.integration.test.ts +12 -2
- package/src/lib/subagent/define.ts +1 -1
- package/src/lib/subagent/handler.ts +186 -71
- package/src/lib/subagent/index.ts +1 -5
- package/src/lib/subagent/register.ts +3 -2
- package/src/lib/subagent/signals.ts +1 -10
- package/src/lib/subagent/subagent.integration.test.ts +438 -156
- package/src/lib/subagent/tool.ts +4 -3
- package/src/lib/subagent/types.ts +50 -20
- package/src/lib/subagent/workflow.ts +9 -49
- package/src/lib/thread/id.test.ts +1 -1
- package/src/lib/thread/id.ts +1 -2
- package/src/lib/thread/proxy.ts +3 -4
- package/src/lib/thread/types.ts +11 -3
- package/src/lib/tool-router/index.ts +1 -5
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +1 -1
- package/src/lib/tool-router/router.ts +3 -2
- package/src/lib/tool-router/types.ts +11 -3
- package/src/lib/tool-router/with-sandbox.ts +19 -5
- package/src/lib/virtual-fs/filesystem.ts +1 -1
- package/src/lib/virtual-fs/index.ts +5 -1
- package/src/lib/virtual-fs/mutations.ts +2 -4
- package/src/lib/virtual-fs/queries.ts +9 -5
- package/src/lib/virtual-fs/types.ts +4 -1
- package/src/lib/virtual-fs/virtual-fs.test.ts +9 -11
- package/src/lib/workflow.test.ts +7 -4
- package/src/lib/workflow.ts +1 -5
- package/src/tools/ask-user-question/tool.ts +1 -3
- package/src/tools/glob/handler.ts +1 -4
- package/src/tools/task-get/handler.ts +4 -5
- package/src/tools/task-list/handler.ts +1 -4
- package/src/tools/task-update/handler.ts +4 -5
- package/src/workflow.ts +20 -7
- package/tsup.config.ts +9 -6
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
|
-
startChild,
|
|
3
2
|
workflowInfo,
|
|
4
3
|
setHandler,
|
|
5
4
|
condition,
|
|
6
5
|
log,
|
|
6
|
+
ApplicationFailure,
|
|
7
|
+
executeChild,
|
|
7
8
|
} from "@temporalio/workflow";
|
|
8
9
|
import { getShortId } from "../thread/id";
|
|
9
10
|
import type { ToolHandlerResponse, RouterContext } from "../tool-router";
|
|
@@ -11,7 +12,6 @@ import type { JsonValue } from "../state/types";
|
|
|
11
12
|
import type {
|
|
12
13
|
InferSubagentResult,
|
|
13
14
|
SubagentConfig,
|
|
14
|
-
SubagentHandlerResponse,
|
|
15
15
|
SubagentSandboxConfig,
|
|
16
16
|
SubagentWorkflowInput,
|
|
17
17
|
} from "./types";
|
|
@@ -22,17 +22,14 @@ import type {
|
|
|
22
22
|
SandboxInit,
|
|
23
23
|
SubagentSandboxShutdown,
|
|
24
24
|
} from "../lifecycle";
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
childSandboxReadySignal,
|
|
28
|
-
destroySandboxSignal,
|
|
29
|
-
} from "./signals";
|
|
25
|
+
import type { SandboxOps, SandboxSnapshot } from "../sandbox/types";
|
|
26
|
+
import { childSandboxReadySignal } from "./signals";
|
|
30
27
|
|
|
31
28
|
/** Normalized sandbox config after resolving the union. */
|
|
32
29
|
interface ResolvedSandboxConfig {
|
|
33
30
|
source: "none" | "inherit" | "own";
|
|
34
31
|
init: "per-call" | "once";
|
|
35
|
-
continuation: "continue" | "fork";
|
|
32
|
+
continuation: "continue" | "fork" | "snapshot";
|
|
36
33
|
shutdown?: SubagentSandboxShutdown;
|
|
37
34
|
}
|
|
38
35
|
|
|
@@ -61,9 +58,9 @@ function resolveSandboxConfig(
|
|
|
61
58
|
/**
|
|
62
59
|
* Creates a Subagent tool handler that spawns child workflows for configured subagents.
|
|
63
60
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
61
|
+
* Sandbox and snapshot cleanup happens inside the parent via each subagent's
|
|
62
|
+
* `sandbox.proxy` — the proxy factory is invoked once per subagent with
|
|
63
|
+
* `scope = agentName` so it resolves to the same activities the child uses.
|
|
67
64
|
*
|
|
68
65
|
* @param subagents - Array of subagent configurations
|
|
69
66
|
* @returns A tool handler function that can be used with the tool router
|
|
@@ -78,13 +75,27 @@ export function createSubagentHandler<
|
|
|
78
75
|
context: RouterContext
|
|
79
76
|
) => Promise<ToolHandlerResponse<InferSubagentResult<T[number]> | null>>;
|
|
80
77
|
destroySubagentSandboxes: () => Promise<void>;
|
|
78
|
+
cleanupSubagentSnapshots: () => Promise<void>;
|
|
81
79
|
} {
|
|
82
80
|
const { taskQueue: parentTaskQueue } = workflowInfo();
|
|
83
81
|
|
|
84
|
-
|
|
82
|
+
/** Sandbox ops proxy per subagent, built eagerly from `sandbox.proxy` factories. */
|
|
83
|
+
const agentSandboxOps = new Map<string, SandboxOps>();
|
|
84
|
+
for (const cfg of subagents) {
|
|
85
|
+
if (cfg.sandbox && cfg.sandbox !== "none") {
|
|
86
|
+
agentSandboxOps.set(cfg.agentName, cfg.sandbox.proxy(cfg.agentName));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Sandboxes that outlived their child session and must be destroyed by the
|
|
92
|
+
* parent at shutdown (shutdown = `pause-until-parent-close` /
|
|
93
|
+
* `keep-until-parent-close`). Keyed by `persistent:<agent>` for lazy
|
|
94
|
+
* shared sandboxes and by childWorkflowId otherwise.
|
|
95
|
+
*/
|
|
85
96
|
const pendingDestroys = new Map<
|
|
86
97
|
string,
|
|
87
|
-
|
|
98
|
+
{ agentName: string; sandboxId: string }
|
|
88
99
|
>();
|
|
89
100
|
/** Maps childThreadId → sandboxId for sandbox continuation across invocations (init: per-call) */
|
|
90
101
|
const threadSandboxes = new Map<string, string>();
|
|
@@ -94,21 +105,26 @@ export function createSubagentHandler<
|
|
|
94
105
|
const persistentSandboxCreating = new Set<string>();
|
|
95
106
|
/** Reverse lookup: childWorkflowId → agentName for in-flight lazy creators */
|
|
96
107
|
const lazyCreatorAgent = new Map<string, string>();
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
}
|
|
108
|
+
/** Maps childThreadId → latest snapshot for sandbox continuation via snapshots */
|
|
109
|
+
const threadSnapshots = new Map<
|
|
110
|
+
string,
|
|
111
|
+
{
|
|
112
|
+
agentName: string;
|
|
113
|
+
snapshot: SandboxSnapshot;
|
|
110
114
|
}
|
|
111
|
-
);
|
|
115
|
+
>();
|
|
116
|
+
/** Maps agentName → reusable base snapshot captured on first-ever call (init: once + continuation: "snapshot") */
|
|
117
|
+
const persistentBaseSnapshot = new Map<string, SandboxSnapshot>();
|
|
118
|
+
/** Tracks agents whose first snapshot-backed sandbox creation is in-flight */
|
|
119
|
+
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);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
112
128
|
|
|
113
129
|
const handler = async (
|
|
114
130
|
args: SubagentArgs,
|
|
@@ -127,6 +143,16 @@ export function createSubagentHandler<
|
|
|
127
143
|
const { sandboxId: parentSandboxId } = context;
|
|
128
144
|
const sandboxCfg = resolveSandboxConfig(config.sandbox);
|
|
129
145
|
|
|
146
|
+
if (
|
|
147
|
+
sandboxCfg.source !== "none" &&
|
|
148
|
+
!agentSandboxOps.has(config.agentName)
|
|
149
|
+
) {
|
|
150
|
+
throw ApplicationFailure.create({
|
|
151
|
+
message: `Subagent "${config.agentName}" uses a sandbox but no \`sandbox.proxy\` is configured on its SubagentConfig`,
|
|
152
|
+
nonRetryable: true,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
130
156
|
if (sandboxCfg.source === "inherit" && !parentSandboxId) {
|
|
131
157
|
throw new Error(
|
|
132
158
|
`Subagent "${config.agentName}" is configured with sandbox: "inherit" but the parent has no sandbox`
|
|
@@ -151,13 +177,50 @@ export function createSubagentHandler<
|
|
|
151
177
|
let sandbox: SandboxInit | undefined;
|
|
152
178
|
let sandboxShutdownOverride: SubagentSandboxShutdown | undefined;
|
|
153
179
|
let isLazyCreator = false;
|
|
180
|
+
let isSnapshotBaseCreator = false;
|
|
154
181
|
|
|
155
182
|
if (sandboxCfg.source === "inherit" && parentSandboxId) {
|
|
156
183
|
if (sandboxCfg.continuation === "fork") {
|
|
157
184
|
sandbox = { mode: "fork", sandboxId: parentSandboxId };
|
|
185
|
+
} else if (sandboxCfg.continuation === "snapshot") {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Subagent "${config.agentName}" has sandbox source "inherit" with continuation "snapshot" — snapshot continuation is only supported for source "own"`
|
|
188
|
+
);
|
|
158
189
|
} else {
|
|
159
190
|
sandbox = { mode: "inherit", sandboxId: parentSandboxId };
|
|
160
191
|
}
|
|
192
|
+
} else if (
|
|
193
|
+
sandboxCfg.source === "own" &&
|
|
194
|
+
sandboxCfg.continuation === "snapshot"
|
|
195
|
+
) {
|
|
196
|
+
// Snapshot-driven continuation: each call boots a fresh sandbox from a
|
|
197
|
+
// stored snapshot (per-thread, or a per-agent base for new threads with
|
|
198
|
+
// init: "once"). The session destroys its sandbox inline on exit;
|
|
199
|
+
// stored snapshot IDs are cleaned up by the parent at shutdown.
|
|
200
|
+
const isLazy = sandboxCfg.init === "once";
|
|
201
|
+
|
|
202
|
+
let baseSnap: SandboxSnapshot | undefined;
|
|
203
|
+
if (continuationThreadId) {
|
|
204
|
+
baseSnap = threadSnapshots.get(continuationThreadId)?.snapshot;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!baseSnap && isLazy) {
|
|
208
|
+
baseSnap = persistentBaseSnapshot.get(config.agentName);
|
|
209
|
+
if (!baseSnap) {
|
|
210
|
+
if (persistentBaseSnapshotCreating.has(config.agentName)) {
|
|
211
|
+
await condition(() => persistentBaseSnapshot.has(config.agentName));
|
|
212
|
+
baseSnap = persistentBaseSnapshot.get(config.agentName);
|
|
213
|
+
} else {
|
|
214
|
+
persistentBaseSnapshotCreating.add(config.agentName);
|
|
215
|
+
isSnapshotBaseCreator = true;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (baseSnap) {
|
|
221
|
+
sandbox = { mode: "from-snapshot", snapshot: baseSnap };
|
|
222
|
+
}
|
|
223
|
+
sandboxShutdownOverride = "snapshot";
|
|
161
224
|
} else if (sandboxCfg.source === "own") {
|
|
162
225
|
const isLazy = sandboxCfg.init === "once";
|
|
163
226
|
|
|
@@ -246,45 +309,11 @@ export function createSubagentHandler<
|
|
|
246
309
|
sandboxSource: sandboxCfg.source,
|
|
247
310
|
});
|
|
248
311
|
|
|
249
|
-
const
|
|
312
|
+
const childResult = await executeChild(config.workflow, childOpts);
|
|
250
313
|
|
|
251
|
-
// Track child handles that need signaling at parent shutdown.
|
|
252
314
|
const effectiveShutdown =
|
|
253
315
|
sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
|
|
254
316
|
|
|
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);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Wait for signal from child; race with child completion to propagate failures
|
|
266
|
-
await Promise.race([
|
|
267
|
-
condition(() => childResults.has(childWorkflowId)),
|
|
268
|
-
childHandle.result(),
|
|
269
|
-
]);
|
|
270
|
-
if (!childResults.has(childWorkflowId)) {
|
|
271
|
-
await condition(() => childResults.has(childWorkflowId));
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const childResult = childResults.get(childWorkflowId);
|
|
275
|
-
childResults.delete(childWorkflowId);
|
|
276
|
-
|
|
277
|
-
if (!childResult) {
|
|
278
|
-
log.warn("subagent returned no result", {
|
|
279
|
-
subagent: config.agentName,
|
|
280
|
-
childWorkflowId,
|
|
281
|
-
});
|
|
282
|
-
return {
|
|
283
|
-
toolResponse: "Subagent workflow did not signal a result",
|
|
284
|
-
data: null,
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
|
|
288
317
|
log.info("subagent completed", {
|
|
289
318
|
subagent: config.agentName,
|
|
290
319
|
childWorkflowId,
|
|
@@ -297,27 +326,71 @@ export function createSubagentHandler<
|
|
|
297
326
|
usage,
|
|
298
327
|
threadId: childThreadId,
|
|
299
328
|
sandboxId: childSandboxId,
|
|
329
|
+
snapshot: childSnapshot,
|
|
330
|
+
baseSnapshot: childBaseSnapshot,
|
|
300
331
|
metadata,
|
|
301
332
|
} = childResult;
|
|
302
333
|
|
|
303
|
-
// Store sandbox ID for future continuation/fork
|
|
304
334
|
if (childSandboxId) {
|
|
305
335
|
if (
|
|
306
336
|
sandboxCfg.source === "own" &&
|
|
307
337
|
sandboxCfg.init === "once" &&
|
|
338
|
+
sandboxCfg.continuation !== "snapshot" &&
|
|
308
339
|
!persistentSandboxes.has(config.agentName)
|
|
309
340
|
) {
|
|
310
341
|
// Fallback: signal may have already set this via childSandboxReadySignal
|
|
311
342
|
persistentSandboxes.set(config.agentName, childSandboxId);
|
|
312
|
-
} else if (
|
|
343
|
+
} else if (
|
|
344
|
+
allowsContinuation &&
|
|
345
|
+
childThreadId &&
|
|
346
|
+
sandboxCfg.source === "own" &&
|
|
347
|
+
sandboxCfg.continuation !== "snapshot"
|
|
348
|
+
) {
|
|
313
349
|
threadSandboxes.set(childThreadId, childSandboxId);
|
|
314
350
|
}
|
|
315
351
|
}
|
|
316
352
|
|
|
353
|
+
// Track sandboxes that must be destroyed by the parent at shutdown.
|
|
354
|
+
if (
|
|
355
|
+
childSandboxId &&
|
|
356
|
+
(effectiveShutdown === "pause-until-parent-close" ||
|
|
357
|
+
effectiveShutdown === "keep-until-parent-close")
|
|
358
|
+
) {
|
|
359
|
+
const key = isLazyCreator
|
|
360
|
+
? `persistent:${config.agentName}`
|
|
361
|
+
: childWorkflowId;
|
|
362
|
+
pendingDestroys.set(key, {
|
|
363
|
+
agentName: config.agentName,
|
|
364
|
+
sandboxId: childSandboxId,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Store snapshots for future snapshot-driven continuation and final sweep.
|
|
369
|
+
// Tag each with `agentName` so `cleanupSubagentSnapshots` knows which
|
|
370
|
+
// sandbox ops to call for deletion.
|
|
371
|
+
if (sandboxCfg.source === "own" && sandboxCfg.continuation === "snapshot") {
|
|
372
|
+
if (childSnapshot && childThreadId) {
|
|
373
|
+
threadSnapshots.set(childThreadId, {
|
|
374
|
+
agentName: config.agentName,
|
|
375
|
+
snapshot: childSnapshot,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
if (
|
|
379
|
+
isSnapshotBaseCreator &&
|
|
380
|
+
childBaseSnapshot &&
|
|
381
|
+
!persistentBaseSnapshot.has(config.agentName)
|
|
382
|
+
) {
|
|
383
|
+
persistentBaseSnapshot.set(config.agentName, childBaseSnapshot);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
317
387
|
if (isLazyCreator) {
|
|
318
388
|
persistentSandboxCreating.delete(config.agentName);
|
|
319
389
|
lazyCreatorAgent.delete(childWorkflowId);
|
|
320
390
|
}
|
|
391
|
+
if (isSnapshotBaseCreator) {
|
|
392
|
+
persistentBaseSnapshotCreating.delete(config.agentName);
|
|
393
|
+
}
|
|
321
394
|
|
|
322
395
|
if (!toolResponse) {
|
|
323
396
|
return {
|
|
@@ -355,7 +428,9 @@ export function createSubagentHandler<
|
|
|
355
428
|
|
|
356
429
|
return {
|
|
357
430
|
toolResponse: finalToolResponse,
|
|
358
|
-
data: validated
|
|
431
|
+
data: validated
|
|
432
|
+
? validated.data
|
|
433
|
+
: (data as InferSubagentResult<T[number]> | null),
|
|
359
434
|
...(usage && { usage }),
|
|
360
435
|
...(childSandboxId && { sandboxId: childSandboxId }),
|
|
361
436
|
...(metadata && { metadata }),
|
|
@@ -363,15 +438,55 @@ export function createSubagentHandler<
|
|
|
363
438
|
};
|
|
364
439
|
|
|
365
440
|
const destroySubagentSandboxes = async (): Promise<void> => {
|
|
366
|
-
const
|
|
441
|
+
const entries = [...pendingDestroys.values()];
|
|
367
442
|
pendingDestroys.clear();
|
|
368
443
|
await Promise.all(
|
|
369
|
-
|
|
444
|
+
entries.map(async ({ agentName, sandboxId }) => {
|
|
445
|
+
const ops = agentSandboxOps.get(agentName);
|
|
446
|
+
if (!ops) {
|
|
447
|
+
log.warn(
|
|
448
|
+
"Skipping sandbox destroy — no sandbox.proxy registered for agent",
|
|
449
|
+
{ agentName, sandboxId }
|
|
450
|
+
);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
await ops.destroySandbox(sandboxId);
|
|
455
|
+
} catch (err) {
|
|
456
|
+
log.warn("Failed to destroy subagent sandbox", {
|
|
457
|
+
agentName,
|
|
458
|
+
sandboxId,
|
|
459
|
+
error: err,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
})
|
|
463
|
+
);
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const cleanupSubagentSnapshots = async (): Promise<void> => {
|
|
467
|
+
const tagged = [];
|
|
468
|
+
for (const entry of threadSnapshots.values()) tagged.push(entry);
|
|
469
|
+
for (const [agentName, snapshot] of persistentBaseSnapshot.entries()) {
|
|
470
|
+
tagged.push({ agentName, snapshot });
|
|
471
|
+
}
|
|
472
|
+
threadSnapshots.clear();
|
|
473
|
+
persistentBaseSnapshot.clear();
|
|
474
|
+
|
|
475
|
+
await Promise.all(
|
|
476
|
+
tagged.map(async ({ agentName, snapshot }) => {
|
|
477
|
+
const ops = agentSandboxOps.get(agentName);
|
|
478
|
+
if (!ops) {
|
|
479
|
+
log.warn(
|
|
480
|
+
"Skipping snapshot delete — no sandbox.proxy registered for agent",
|
|
481
|
+
{ agentName }
|
|
482
|
+
);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
370
485
|
try {
|
|
371
|
-
await
|
|
372
|
-
await handle.result();
|
|
486
|
+
await ops.deleteSandboxSnapshot(snapshot);
|
|
373
487
|
} catch (err) {
|
|
374
|
-
log.warn("Failed to
|
|
488
|
+
log.warn("Failed to delete subagent snapshot", {
|
|
489
|
+
agentName,
|
|
375
490
|
error: err,
|
|
376
491
|
});
|
|
377
492
|
}
|
|
@@ -379,5 +494,5 @@ export function createSubagentHandler<
|
|
|
379
494
|
);
|
|
380
495
|
};
|
|
381
496
|
|
|
382
|
-
return { handler, destroySubagentSandboxes };
|
|
497
|
+
return { handler, destroySubagentSandboxes, cleanupSubagentSnapshots };
|
|
383
498
|
}
|
|
@@ -17,8 +17,4 @@ export { createSubagentHandler } from "./handler";
|
|
|
17
17
|
export { defineSubagent } from "./define";
|
|
18
18
|
export { defineSubagentWorkflow } from "./workflow";
|
|
19
19
|
export { buildSubagentRegistration } from "./register";
|
|
20
|
-
export {
|
|
21
|
-
childResultSignal,
|
|
22
|
-
childSandboxReadySignal,
|
|
23
|
-
destroySandboxSignal,
|
|
24
|
-
} from "./signals";
|
|
20
|
+
export { childSandboxReadySignal } from "./signals";
|
|
@@ -26,6 +26,7 @@ import { createSubagentHandler } from "./handler";
|
|
|
26
26
|
export function buildSubagentRegistration(subagents: SubagentConfig[]): {
|
|
27
27
|
registration: ToolMap[string];
|
|
28
28
|
destroySubagentSandboxes: () => Promise<void>;
|
|
29
|
+
cleanupSubagentSnapshots: () => Promise<void>;
|
|
29
30
|
} | null {
|
|
30
31
|
if (subagents.length === 0) return null;
|
|
31
32
|
|
|
@@ -42,7 +43,7 @@ export function buildSubagentRegistration(subagents: SubagentConfig[]): {
|
|
|
42
43
|
const resolveSubagentName = (args: unknown): string =>
|
|
43
44
|
(args as SubagentArgs).subagent;
|
|
44
45
|
|
|
45
|
-
const { handler, destroySubagentSandboxes } =
|
|
46
|
+
const { handler, destroySubagentSandboxes, cleanupSubagentSnapshots } =
|
|
46
47
|
createSubagentHandler(subagents);
|
|
47
48
|
|
|
48
49
|
const registration: ToolMap[string] = {
|
|
@@ -72,5 +73,5 @@ export function buildSubagentRegistration(subagents: SubagentConfig[]): {
|
|
|
72
73
|
}),
|
|
73
74
|
};
|
|
74
75
|
|
|
75
|
-
return { registration, destroySubagentSandboxes };
|
|
76
|
+
return { registration, destroySubagentSandboxes, cleanupSubagentSnapshots };
|
|
76
77
|
}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
import { defineSignal } from "@temporalio/workflow";
|
|
2
|
-
import type {
|
|
3
|
-
ChildResultSignalPayload,
|
|
4
|
-
ChildSandboxReadySignalPayload,
|
|
5
|
-
} from "./types";
|
|
6
|
-
|
|
7
|
-
export const childResultSignal =
|
|
8
|
-
defineSignal<[ChildResultSignalPayload]>("childResult");
|
|
2
|
+
import type { ChildSandboxReadySignalPayload } from "./types";
|
|
9
3
|
|
|
10
4
|
/** Sent by a child workflow as soon as its sandbox is created, before the agent loop starts. */
|
|
11
5
|
export const childSandboxReadySignal =
|
|
12
6
|
defineSignal<[ChildSandboxReadySignalPayload]>("childSandboxReady");
|
|
13
|
-
|
|
14
|
-
/** Sent by the parent to tell a subagent it may destroy its sandbox. */
|
|
15
|
-
export const destroySandboxSignal = defineSignal("destroySandbox");
|