zeitlich 0.2.33 → 0.2.35
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 +17 -6
- package/dist/{activities-YBD5BaHh.d.ts → activities-BVI2lTwr.d.ts} +6 -4
- package/dist/{activities-fnX8-vhR.d.cts → activities-hd4aNnZE.d.cts} +6 -4
- package/dist/adapters/sandbox/bedrock/index.cjs +2 -0
- 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 +2 -0
- package/dist/adapters/sandbox/bedrock/index.js.map +1 -1
- package/dist/adapters/sandbox/bedrock/workflow.cjs +1 -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 +1 -0
- package/dist/adapters/sandbox/bedrock/workflow.js.map +1 -1
- package/dist/adapters/sandbox/daytona/index.cjs +2 -0
- package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
- package/dist/adapters/sandbox/daytona/index.d.cts +2 -1
- package/dist/adapters/sandbox/daytona/index.d.ts +2 -1
- package/dist/adapters/sandbox/daytona/index.js +2 -0
- package/dist/adapters/sandbox/daytona/index.js.map +1 -1
- package/dist/adapters/sandbox/daytona/workflow.cjs +1 -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 +1 -0
- package/dist/adapters/sandbox/daytona/workflow.js.map +1 -1
- package/dist/adapters/sandbox/e2b/index.cjs +3 -0
- package/dist/adapters/sandbox/e2b/index.cjs.map +1 -1
- package/dist/adapters/sandbox/e2b/index.d.cts +2 -1
- package/dist/adapters/sandbox/e2b/index.d.ts +2 -1
- package/dist/adapters/sandbox/e2b/index.js +3 -0
- package/dist/adapters/sandbox/e2b/index.js.map +1 -1
- package/dist/adapters/sandbox/e2b/workflow.cjs +1 -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 +1 -0
- package/dist/adapters/sandbox/e2b/workflow.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/index.cjs +2 -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 +2 -0
- package/dist/adapters/sandbox/inmemory/index.js.map +1 -1
- package/dist/adapters/sandbox/inmemory/workflow.cjs +1 -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 +1 -0
- package/dist/adapters/sandbox/inmemory/workflow.js.map +1 -1
- package/dist/adapters/thread/anthropic/index.cjs +7 -2
- 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 +7 -2
- package/dist/adapters/thread/anthropic/index.js.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/google-genai/index.cjs +4 -3
- 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 +4 -3
- package/dist/adapters/thread/google-genai/index.js.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/langchain/index.cjs +4 -1
- 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 +4 -1
- package/dist/adapters/thread/langchain/index.js.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/index.cjs +115 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -9
- package/dist/index.d.ts +10 -9
- package/dist/index.js +115 -31
- package/dist/index.js.map +1 -1
- package/dist/{proxy-CTCYWjkr.d.cts → proxy-7RnVaPdJ.d.cts} +1 -1
- package/dist/{proxy-Br4unLTC.d.ts → proxy-BjdFGPTm.d.ts} +1 -1
- package/dist/{thread-manager-DKWxHUzD.d.ts → thread-manager-BBzNgQWH.d.cts} +5 -2
- package/dist/{thread-manager-CUubPYPH.d.cts → thread-manager-CbpiGq1L.d.ts} +6 -3
- package/dist/{thread-manager-Cv_BR28i.d.cts → thread-manager-DjN5JYul.d.ts} +5 -2
- package/dist/{thread-manager-YJLoc1vH.d.ts → thread-manager-DzXm9eeI.d.cts} +6 -3
- package/dist/{types-Bpq5fDI5.d.cts → types-CADc5V_P.d.ts} +39 -24
- package/dist/{types-DUvEZSDe.d.cts → types-CBH54cwr.d.cts} +1 -1
- package/dist/{types-CheCTLeV.d.ts → types-DQ1l_gXL.d.cts} +39 -24
- package/dist/{types-AujBIMMn.d.cts → types-DxCpFNv_.d.cts} +4 -0
- package/dist/{types-AujBIMMn.d.ts → types-DxCpFNv_.d.ts} +4 -0
- package/dist/{types-NJDyMyUx.d.cts → types-Mc_4BCfT.d.cts} +3 -3
- package/dist/{types-DBk-C8zM.d.ts → types-wiGLvxWf.d.ts} +1 -1
- package/dist/{types-BxiT8w9d.d.ts → types-yiXmqedU.d.ts} +3 -3
- package/dist/{workflow-Od9vx5Jk.d.cts → workflow-DhtWRovz.d.cts} +3 -3
- package/dist/{workflow-D9nNERvs.d.ts → workflow-P2pTSfKu.d.ts} +3 -3
- package/dist/workflow.cjs +109 -31
- 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 +109 -31
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/sandbox/bedrock/index.ts +4 -0
- package/src/adapters/sandbox/bedrock/proxy.ts +1 -0
- package/src/adapters/sandbox/daytona/index.ts +4 -0
- package/src/adapters/sandbox/daytona/proxy.ts +1 -0
- package/src/adapters/sandbox/e2b/index.ts +4 -0
- package/src/adapters/sandbox/e2b/proxy.ts +1 -0
- package/src/adapters/sandbox/inmemory/index.ts +4 -0
- package/src/adapters/sandbox/inmemory/proxy.ts +1 -0
- package/src/adapters/thread/anthropic/activities.ts +2 -1
- package/src/adapters/thread/anthropic/thread-manager.ts +21 -9
- package/src/adapters/thread/google-genai/activities.ts +2 -1
- package/src/adapters/thread/google-genai/thread-manager.test.ts +1 -1
- package/src/adapters/thread/google-genai/thread-manager.ts +15 -7
- package/src/adapters/thread/langchain/activities.ts +2 -1
- package/src/adapters/thread/langchain/thread-manager.ts +12 -3
- package/src/lib/lifecycle.ts +7 -3
- package/src/lib/sandbox/manager.ts +7 -0
- package/src/lib/sandbox/types.ts +4 -0
- package/src/lib/session/session-edge-cases.integration.test.ts +194 -0
- package/src/lib/session/session.integration.test.ts +5 -0
- package/src/lib/session/session.ts +13 -1
- package/src/lib/session/types.ts +10 -2
- package/src/lib/state/manager.ts +2 -2
- package/src/lib/state/types.ts +2 -2
- package/src/lib/subagent/define.ts +1 -1
- package/src/lib/subagent/handler.ts +142 -32
- package/src/lib/subagent/index.ts +5 -1
- package/src/lib/subagent/signals.ts +8 -1
- package/src/lib/subagent/subagent.integration.test.ts +532 -25
- package/src/lib/subagent/types.ts +32 -15
- package/src/lib/subagent/workflow.ts +26 -13
- package/src/lib/thread/types.ts +2 -2
- package/src/lib/types.ts +1 -1
- package/src/lib/virtual-fs/manager.ts +1 -1
- package/src/lib/virtual-fs/types.ts +2 -2
- package/src/lib/virtual-fs/virtual-fs.test.ts +2 -2
- package/src/workflow.ts +0 -1
|
@@ -518,6 +518,7 @@ describe("createSession integration", () => {
|
|
|
518
518
|
}),
|
|
519
519
|
forkSandbox: async () => "forked-sandbox-id",
|
|
520
520
|
pauseSandbox: async () => {},
|
|
521
|
+
resumeSandbox: async () => {},
|
|
521
522
|
};
|
|
522
523
|
|
|
523
524
|
const session = await createSession({
|
|
@@ -559,6 +560,7 @@ describe("createSession integration", () => {
|
|
|
559
560
|
}),
|
|
560
561
|
forkSandbox: async () => "forked-sandbox-id",
|
|
561
562
|
pauseSandbox: async () => {},
|
|
563
|
+
resumeSandbox: async () => {},
|
|
562
564
|
};
|
|
563
565
|
|
|
564
566
|
const session = await createSession({
|
|
@@ -600,6 +602,7 @@ describe("createSession integration", () => {
|
|
|
600
602
|
createSandbox: async () => ({ sandboxId: "sb" }),
|
|
601
603
|
destroySandbox: async () => {},
|
|
602
604
|
pauseSandbox: async () => {},
|
|
605
|
+
resumeSandbox: async () => {},
|
|
603
606
|
snapshotSandbox: async () => ({
|
|
604
607
|
sandboxId: "sb",
|
|
605
608
|
providerId: "test",
|
|
@@ -820,6 +823,7 @@ describe("createSession integration", () => {
|
|
|
820
823
|
}),
|
|
821
824
|
forkSandbox: async () => "forked-sandbox-id",
|
|
822
825
|
pauseSandbox: async () => {},
|
|
826
|
+
resumeSandbox: async () => {},
|
|
823
827
|
};
|
|
824
828
|
|
|
825
829
|
const session = await createSession({
|
|
@@ -870,6 +874,7 @@ describe("createSession integration", () => {
|
|
|
870
874
|
}),
|
|
871
875
|
forkSandbox: async () => "forked-sandbox-id",
|
|
872
876
|
pauseSandbox: async () => {},
|
|
877
|
+
resumeSandbox: async () => {},
|
|
873
878
|
};
|
|
874
879
|
|
|
875
880
|
const session = await createSession({
|
|
@@ -106,6 +106,7 @@ export async function createSession<
|
|
|
106
106
|
thread: threadInit,
|
|
107
107
|
sandbox: sandboxInit,
|
|
108
108
|
sandboxShutdown = "destroy",
|
|
109
|
+
onSandboxReady,
|
|
109
110
|
virtualFs: virtualFsConfig,
|
|
110
111
|
virtualFsOps,
|
|
111
112
|
}: SessionConfig<T, M, TContent>): Promise<ZeitlichSession<M, boolean>> {
|
|
@@ -236,6 +237,9 @@ export async function createSession<
|
|
|
236
237
|
}
|
|
237
238
|
sandboxId = (sandboxInit as { mode: "continue"; sandboxId: string })
|
|
238
239
|
.sandboxId;
|
|
240
|
+
if (sandboxShutdown === "pause-until-parent-close") {
|
|
241
|
+
await sandboxOps.resumeSandbox(sandboxId);
|
|
242
|
+
}
|
|
239
243
|
sandboxOwned = true;
|
|
240
244
|
} else if (sandboxMode === "fork") {
|
|
241
245
|
if (!sandboxOps) {
|
|
@@ -262,6 +266,10 @@ export async function createSession<
|
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
268
|
|
|
269
|
+
if (sandboxId && onSandboxReady) {
|
|
270
|
+
onSandboxReady(sandboxId);
|
|
271
|
+
}
|
|
272
|
+
|
|
265
273
|
// --- Virtual filesystem init (independent of sandbox) ----------------
|
|
266
274
|
if (virtualFsConfig) {
|
|
267
275
|
if (!virtualFsOps) {
|
|
@@ -317,7 +325,10 @@ export async function createSession<
|
|
|
317
325
|
// "continue" — thread already exists, just append the new message
|
|
318
326
|
} else {
|
|
319
327
|
if (appendSystemPrompt) {
|
|
320
|
-
if (
|
|
328
|
+
if (
|
|
329
|
+
systemPrompt == null ||
|
|
330
|
+
(typeof systemPrompt === "string" && systemPrompt.trim() === "")
|
|
331
|
+
) {
|
|
321
332
|
throw ApplicationFailure.create({
|
|
322
333
|
message: "No system prompt in state",
|
|
323
334
|
nonRetryable: true,
|
|
@@ -467,6 +478,7 @@ export async function createSession<
|
|
|
467
478
|
await sandboxOps.pauseSandbox(sandboxId);
|
|
468
479
|
break;
|
|
469
480
|
case "keep":
|
|
481
|
+
case "keep-until-parent-close":
|
|
470
482
|
break;
|
|
471
483
|
}
|
|
472
484
|
}
|
package/src/lib/session/types.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Duration } from "@temporalio/common";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
SessionExitReason,
|
|
4
|
+
ToolResultConfig,
|
|
5
|
+
} from "../types";
|
|
3
6
|
import type {
|
|
4
7
|
ToolMap,
|
|
5
8
|
ToolCallResultUnion,
|
|
@@ -50,7 +53,7 @@ export interface ThreadOps<TContent = string> {
|
|
|
50
53
|
appendSystemMessage(
|
|
51
54
|
threadId: string,
|
|
52
55
|
id: string,
|
|
53
|
-
content:
|
|
56
|
+
content: unknown,
|
|
54
57
|
threadKey?: string
|
|
55
58
|
): Promise<void>;
|
|
56
59
|
/** Copy all messages from sourceThreadId into a new thread at targetThreadId */
|
|
@@ -180,6 +183,11 @@ export interface SessionConfig<
|
|
|
180
183
|
* Has no effect when the sandbox is inherited (`sandbox.mode === "inherit"`).
|
|
181
184
|
*/
|
|
182
185
|
sandboxShutdown?: SubagentSandboxShutdown;
|
|
186
|
+
/**
|
|
187
|
+
* Called as soon as the sandbox is created (or resumed/forked), before the
|
|
188
|
+
* agent loop starts. Useful for signalling sandbox readiness to a parent.
|
|
189
|
+
*/
|
|
190
|
+
onSandboxReady?: (sandboxId: string) => void;
|
|
183
191
|
|
|
184
192
|
// ---------------------------------------------------------------------------
|
|
185
193
|
// Virtual filesystem
|
package/src/lib/state/manager.ts
CHANGED
|
@@ -108,7 +108,7 @@ export function createAgentStateManager<
|
|
|
108
108
|
return status === "RUNNING";
|
|
109
109
|
},
|
|
110
110
|
|
|
111
|
-
getSystemPrompt():
|
|
111
|
+
getSystemPrompt(): unknown {
|
|
112
112
|
return systemPrompt;
|
|
113
113
|
},
|
|
114
114
|
|
|
@@ -202,7 +202,7 @@ export function createAgentStateManager<
|
|
|
202
202
|
}));
|
|
203
203
|
},
|
|
204
204
|
|
|
205
|
-
setSystemPrompt(newSystemPrompt:
|
|
205
|
+
setSystemPrompt(newSystemPrompt: unknown): void {
|
|
206
206
|
systemPrompt = newSystemPrompt;
|
|
207
207
|
},
|
|
208
208
|
|
package/src/lib/state/types.ts
CHANGED
|
@@ -90,10 +90,10 @@ export interface AgentStateManager<TCustom extends JsonSerializable<TCustom>> {
|
|
|
90
90
|
getTurns(): number;
|
|
91
91
|
|
|
92
92
|
/** Get the system prompt */
|
|
93
|
-
getSystemPrompt():
|
|
93
|
+
getSystemPrompt(): unknown;
|
|
94
94
|
|
|
95
95
|
/** Set the system prompt */
|
|
96
|
-
setSystemPrompt(newSystemPrompt:
|
|
96
|
+
setSystemPrompt(newSystemPrompt: unknown): void;
|
|
97
97
|
|
|
98
98
|
/** Get a custom state value by key */
|
|
99
99
|
get<K extends keyof TCustom>(key: K): TCustom[K];
|
|
@@ -21,7 +21,7 @@ import type { SubagentArgs } from "./tool";
|
|
|
21
21
|
* // With parent-specific overrides
|
|
22
22
|
* export const researcher = defineSubagent(researcherWorkflow, {
|
|
23
23
|
* thread: "fork",
|
|
24
|
-
* sandbox: { source: "own", shutdown: "pause" },
|
|
24
|
+
* sandbox: { source: "own", continuation: "fork", shutdown: "pause" },
|
|
25
25
|
* hooks: {
|
|
26
26
|
* onPostExecution: ({ result }) => console.log(result),
|
|
27
27
|
* },
|
|
@@ -22,19 +22,40 @@ import type {
|
|
|
22
22
|
SandboxInit,
|
|
23
23
|
SubagentSandboxShutdown,
|
|
24
24
|
} from "../lifecycle";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
childResultSignal,
|
|
27
|
+
childSandboxReadySignal,
|
|
28
|
+
destroySandboxSignal,
|
|
29
|
+
} from "./signals";
|
|
26
30
|
|
|
27
|
-
/**
|
|
28
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
116
|
-
mode: "
|
|
117
|
-
|
|
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
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
209
|
-
|
|
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 {
|
|
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 {
|
|
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");
|