zeitlich 0.2.32 → 0.2.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -16
- package/dist/{activities-FIXVz7DT.d.ts → activities-JOqPfKP0.d.cts} +6 -5
- package/dist/{activities-DA-bQM12.d.cts → activities-WwMsjRwm.d.ts} +6 -5
- 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 +18 -2
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +12 -11
- package/dist/adapters/thread/anthropic/index.d.ts +12 -11
- package/dist/adapters/thread/anthropic/index.js +18 -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 +29 -8
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +8 -8
- package/dist/adapters/thread/google-genai/index.d.ts +8 -8
- package/dist/adapters/thread/google-genai/index.js +29 -8
- 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 +42 -23
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +13 -11
- package/dist/adapters/thread/langchain/index.d.ts +13 -11
- package/dist/adapters/thread/langchain/index.js +42 -23
- 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 +148 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -16
- package/dist/index.d.ts +32 -16
- package/dist/index.js +147 -35
- package/dist/index.js.map +1 -1
- package/dist/{proxy-CTCYWjkr.d.cts → proxy-BesT2ioL.d.cts} +1 -1
- package/dist/{proxy-Br4unLTC.d.ts → proxy-Bz6wXYW-.d.ts} +1 -1
- package/dist/{thread-manager-Cv_BR28i.d.cts → thread-manager-CCVAOK8g.d.cts} +1 -1
- package/dist/{thread-manager-CUubPYPH.d.cts → thread-manager-Cf_34H8w.d.cts} +1 -1
- package/dist/{thread-manager-YJLoc1vH.d.ts → thread-manager-ClKAQx78.d.ts} +1 -1
- package/dist/{thread-manager-DKWxHUzD.d.ts → thread-manager-DarJIK_b.d.ts} +1 -1
- package/dist/{types-Bpq5fDI5.d.cts → types-BGLW5Zyj.d.ts} +35 -20
- package/dist/{types-BxiT8w9d.d.ts → types-BVUmLYpj.d.ts} +1 -1
- package/dist/{types-DUvEZSDe.d.cts → types-CBH54cwr.d.cts} +1 -1
- package/dist/{types-NJDyMyUx.d.cts → types-DPAZ3KCs.d.cts} +1 -1
- package/dist/{types-CheCTLeV.d.ts → types-DlLajQcu.d.cts} +35 -20
- 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-DBk-C8zM.d.ts → types-wiGLvxWf.d.ts} +1 -1
- package/dist/{workflow-BWKQcz9d.d.cts → workflow-_ZGcacCK.d.ts} +32 -4
- package/dist/{workflow-D8wK7TJY.d.ts → workflow-hocXpLwg.d.cts} +32 -4
- package/dist/workflow.cjs +126 -30
- 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 +126 -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 +4 -3
- package/src/adapters/thread/anthropic/model-invoker.ts +15 -5
- package/src/adapters/thread/google-genai/activities.ts +4 -3
- package/src/adapters/thread/google-genai/model-invoker.ts +24 -11
- package/src/adapters/thread/langchain/activities.ts +3 -3
- package/src/adapters/thread/langchain/model-invoker.ts +63 -34
- package/src/index.ts +1 -0
- package/src/lib/activity.ts +36 -9
- package/src/lib/lifecycle.ts +7 -3
- package/src/lib/model/helpers.ts +1 -0
- package/src/lib/model/index.ts +1 -0
- package/src/lib/model/proxy.ts +50 -0
- 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 +9 -0
- package/src/lib/session/types.ts +5 -0
- 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/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 +3 -0
- package/src/lib/.env +0 -1
- package/src/tools/bash/.env +0 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it, vi, afterEach } from "vitest";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
| ((payload: { childWorkflowId: string; result: unknown }) => void)
|
|
6
|
-
| null = null;
|
|
4
|
+
const capturedSignalHandlers = new Map<unknown, (...args: unknown[]) => void>();
|
|
7
5
|
|
|
8
6
|
let nextStartChildResult: ((prompt: string) => unknown) | null = null;
|
|
9
7
|
|
|
@@ -35,10 +33,10 @@ vi.mock("@temporalio/workflow", () => {
|
|
|
35
33
|
workflowId: "child-wf-1",
|
|
36
34
|
parent: { workflowId: "parent-wf-1" },
|
|
37
35
|
}),
|
|
38
|
-
defineSignal: vi.fn((
|
|
36
|
+
defineSignal: vi.fn((name: string) => ({ __signal: true, name })),
|
|
39
37
|
setHandler: vi.fn(
|
|
40
|
-
(
|
|
41
|
-
|
|
38
|
+
(signal: unknown, handler: (...a: unknown[]) => void) => {
|
|
39
|
+
capturedSignalHandlers.set(signal, handler);
|
|
42
40
|
}
|
|
43
41
|
),
|
|
44
42
|
condition: vi.fn(async (fn: () => boolean) => {
|
|
@@ -59,8 +57,10 @@ vi.mock("@temporalio/workflow", () => {
|
|
|
59
57
|
usage: { inputTokens: 100, outputTokens: 50 },
|
|
60
58
|
};
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
for (const [signal, handler] of capturedSignalHandlers.entries()) {
|
|
61
|
+
if ((signal as { name?: string }).name === "childResult") {
|
|
62
|
+
handler({ childWorkflowId: opts.workflowId, result });
|
|
63
|
+
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
return {
|
|
@@ -81,7 +81,13 @@ vi.mock("@temporalio/workflow", () => {
|
|
|
81
81
|
).join("");
|
|
82
82
|
return `${bytes.slice(0, 8)}-${bytes.slice(8, 12)}-${bytes.slice(12, 16)}-${bytes.slice(16, 20)}-${bytes.slice(20, 32)}`;
|
|
83
83
|
},
|
|
84
|
-
log: {
|
|
84
|
+
log: {
|
|
85
|
+
trace: () => {},
|
|
86
|
+
debug: () => {},
|
|
87
|
+
info: () => {},
|
|
88
|
+
warn: () => {},
|
|
89
|
+
error: () => {},
|
|
90
|
+
},
|
|
85
91
|
};
|
|
86
92
|
});
|
|
87
93
|
|
|
@@ -98,7 +104,7 @@ import type {
|
|
|
98
104
|
} from "./types";
|
|
99
105
|
afterEach(() => {
|
|
100
106
|
nextStartChildResult = null;
|
|
101
|
-
|
|
107
|
+
capturedSignalHandlers.clear();
|
|
102
108
|
});
|
|
103
109
|
|
|
104
110
|
function mockWorkflow(name?: string): SubagentWorkflow {
|
|
@@ -371,7 +377,7 @@ describe("createSubagentHandler", () => {
|
|
|
371
377
|
agentName: "inherit-agent",
|
|
372
378
|
description: "Inherits sandbox",
|
|
373
379
|
workflow: mockWorkflow(),
|
|
374
|
-
sandbox: "inherit",
|
|
380
|
+
sandbox: { source: "inherit", continuation: "continue" },
|
|
375
381
|
};
|
|
376
382
|
|
|
377
383
|
const { handler } = createSubagentHandler([inheritSubagent]);
|
|
@@ -400,7 +406,7 @@ describe("createSubagentHandler", () => {
|
|
|
400
406
|
agentName: "inherit-agent",
|
|
401
407
|
description: "Inherits sandbox",
|
|
402
408
|
workflow: mockWorkflow(),
|
|
403
|
-
sandbox: "inherit",
|
|
409
|
+
sandbox: { source: "inherit", continuation: "continue" },
|
|
404
410
|
};
|
|
405
411
|
|
|
406
412
|
const { handler } = createSubagentHandler([inheritSubagent]);
|
|
@@ -410,12 +416,10 @@ describe("createSubagentHandler", () => {
|
|
|
410
416
|
{ subagent: "inherit-agent", description: "test", prompt: "test" },
|
|
411
417
|
{ threadId: "t", toolCallId: "tc", toolName: "Subagent" }
|
|
412
418
|
)
|
|
413
|
-
).rejects.toThrow(
|
|
414
|
-
'sandbox: "inherit" but the parent has no sandbox'
|
|
415
|
-
);
|
|
419
|
+
).rejects.toThrow('sandbox: "inherit" but the parent has no sandbox');
|
|
416
420
|
});
|
|
417
421
|
|
|
418
|
-
it("does not pass sandboxId to child when sandbox is own", async () => {
|
|
422
|
+
it("does not pass sandboxId to child when sandbox is own (first call)", async () => {
|
|
419
423
|
const { startChild } = await import("@temporalio/workflow");
|
|
420
424
|
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
421
425
|
|
|
@@ -423,7 +427,7 @@ describe("createSubagentHandler", () => {
|
|
|
423
427
|
agentName: "own-agent",
|
|
424
428
|
description: "Own sandbox",
|
|
425
429
|
workflow: mockWorkflow(),
|
|
426
|
-
sandbox: "own",
|
|
430
|
+
sandbox: { source: "own", continuation: "fork" },
|
|
427
431
|
};
|
|
428
432
|
|
|
429
433
|
const { handler } = createSubagentHandler([ownSubagent]);
|
|
@@ -496,7 +500,7 @@ describe("createSubagentHandler", () => {
|
|
|
496
500
|
expect(context).toEqual({ key: "value" });
|
|
497
501
|
});
|
|
498
502
|
|
|
499
|
-
it("does not pass
|
|
503
|
+
it("does not pass sandbox init when sandbox is own without prior sandbox", async () => {
|
|
500
504
|
const { startChild } = await import("@temporalio/workflow");
|
|
501
505
|
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
502
506
|
|
|
@@ -504,7 +508,7 @@ describe("createSubagentHandler", () => {
|
|
|
504
508
|
agentName: "own-agent",
|
|
505
509
|
description: "Own sandbox",
|
|
506
510
|
workflow: mockWorkflow(),
|
|
507
|
-
sandbox: "own",
|
|
511
|
+
sandbox: { source: "own", continuation: "fork" },
|
|
508
512
|
};
|
|
509
513
|
|
|
510
514
|
const { handler } = createSubagentHandler([ownSubagent]);
|
|
@@ -666,7 +670,7 @@ describe("createSubagentHandler", () => {
|
|
|
666
670
|
description: "Sandbox continuation",
|
|
667
671
|
workflow: mockWorkflow(),
|
|
668
672
|
thread: "fork",
|
|
669
|
-
sandbox: "own",
|
|
673
|
+
sandbox: { source: "own", continuation: "fork" },
|
|
670
674
|
};
|
|
671
675
|
|
|
672
676
|
const { handler } = createSubagentHandler([contSandboxSubagent]);
|
|
@@ -722,7 +726,7 @@ describe("createSubagentHandler", () => {
|
|
|
722
726
|
description: "Sandbox continuation",
|
|
723
727
|
workflow: mockWorkflow(),
|
|
724
728
|
thread: "fork",
|
|
725
|
-
sandbox: "own",
|
|
729
|
+
sandbox: { source: "own", continuation: "fork" },
|
|
726
730
|
};
|
|
727
731
|
|
|
728
732
|
const { handler } = createSubagentHandler([contSandboxSubagent]);
|
|
@@ -789,7 +793,7 @@ describe("createSubagentHandler", () => {
|
|
|
789
793
|
agentName: "own-agent",
|
|
790
794
|
description: "Own sandbox",
|
|
791
795
|
workflow: mockWorkflow(),
|
|
792
|
-
sandbox: "own",
|
|
796
|
+
sandbox: { source: "own", continuation: "fork" },
|
|
793
797
|
};
|
|
794
798
|
|
|
795
799
|
const { handler, destroySubagentSandboxes } = createSubagentHandler([
|
|
@@ -819,7 +823,11 @@ describe("createSubagentHandler", () => {
|
|
|
819
823
|
agentName: "own-agent",
|
|
820
824
|
description: "Own sandbox",
|
|
821
825
|
workflow: mockWorkflow(),
|
|
822
|
-
sandbox: {
|
|
826
|
+
sandbox: {
|
|
827
|
+
source: "own",
|
|
828
|
+
continuation: "fork",
|
|
829
|
+
shutdown: "pause-until-parent-close",
|
|
830
|
+
},
|
|
823
831
|
};
|
|
824
832
|
|
|
825
833
|
const { handler, destroySubagentSandboxes } = createSubagentHandler([
|
|
@@ -847,7 +855,7 @@ describe("createSubagentHandler", () => {
|
|
|
847
855
|
agentName: "inherit-agent",
|
|
848
856
|
description: "Inherits sandbox",
|
|
849
857
|
workflow: mockWorkflow(),
|
|
850
|
-
sandbox: "inherit",
|
|
858
|
+
sandbox: { source: "inherit", continuation: "continue" },
|
|
851
859
|
};
|
|
852
860
|
|
|
853
861
|
const { handler, destroySubagentSandboxes } = createSubagentHandler([
|
|
@@ -961,6 +969,237 @@ describe("createSubagentHandler", () => {
|
|
|
961
969
|
expect(childHandle.signal).not.toHaveBeenCalled();
|
|
962
970
|
});
|
|
963
971
|
|
|
972
|
+
// --- inherit + continuation: fork ---
|
|
973
|
+
|
|
974
|
+
it("forks from parent sandbox when inherit + continuation=fork", async () => {
|
|
975
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
976
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
977
|
+
|
|
978
|
+
const config: SubagentConfig = {
|
|
979
|
+
agentName: "inherit-fork",
|
|
980
|
+
description: "Inherit fork",
|
|
981
|
+
workflow: mockWorkflow(),
|
|
982
|
+
sandbox: { source: "inherit", continuation: "fork" },
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
const { handler } = createSubagentHandler([config]);
|
|
986
|
+
|
|
987
|
+
await handler(
|
|
988
|
+
{ subagent: "inherit-fork", description: "test", prompt: "test" },
|
|
989
|
+
{
|
|
990
|
+
threadId: "t",
|
|
991
|
+
toolCallId: "tc",
|
|
992
|
+
toolName: "Subagent",
|
|
993
|
+
sandboxId: "parent-sb",
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
|
|
997
|
+
const lastCall = startMock.mock.calls.at(-1);
|
|
998
|
+
if (!lastCall) throw new Error("expected startChild call");
|
|
999
|
+
const workflowInput = lastCall[1].args[1] as SubagentWorkflowInput;
|
|
1000
|
+
expect(workflowInput.sandbox).toEqual({
|
|
1001
|
+
mode: "fork",
|
|
1002
|
+
sandboxId: "parent-sb",
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1005
|
+
|
|
1006
|
+
// --- own + continuation: continue ---
|
|
1007
|
+
|
|
1008
|
+
it("passes sandbox continue on thread continuation with continuation=continue", async () => {
|
|
1009
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1010
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1011
|
+
|
|
1012
|
+
nextStartChildResult = () => ({
|
|
1013
|
+
toolResponse: "first",
|
|
1014
|
+
data: null,
|
|
1015
|
+
threadId: "child-t-1",
|
|
1016
|
+
sandboxId: "child-sb-1",
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
const config: SubagentConfig = {
|
|
1020
|
+
agentName: "own-cont",
|
|
1021
|
+
description: "Own continue",
|
|
1022
|
+
workflow: mockWorkflow(),
|
|
1023
|
+
thread: "continue",
|
|
1024
|
+
sandbox: { source: "own", continuation: "continue" },
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
const { handler } = createSubagentHandler([config]);
|
|
1028
|
+
|
|
1029
|
+
await handler(
|
|
1030
|
+
{ subagent: "own-cont", description: "test", prompt: "first" },
|
|
1031
|
+
{ threadId: "t", toolCallId: "tc-1", toolName: "Subagent" }
|
|
1032
|
+
);
|
|
1033
|
+
|
|
1034
|
+
nextStartChildResult = () => ({
|
|
1035
|
+
toolResponse: "second",
|
|
1036
|
+
data: null,
|
|
1037
|
+
threadId: "child-t-1",
|
|
1038
|
+
sandboxId: "child-sb-1",
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
await handler(
|
|
1042
|
+
{
|
|
1043
|
+
subagent: "own-cont",
|
|
1044
|
+
description: "test",
|
|
1045
|
+
prompt: "second",
|
|
1046
|
+
threadId: "child-t-1",
|
|
1047
|
+
},
|
|
1048
|
+
{ threadId: "t", toolCallId: "tc-2", toolName: "Subagent" }
|
|
1049
|
+
);
|
|
1050
|
+
|
|
1051
|
+
const secondCall = startMock.mock.calls.at(-1);
|
|
1052
|
+
if (!secondCall) throw new Error("expected startChild call");
|
|
1053
|
+
const workflowInput = secondCall[1].args[1] as SubagentWorkflowInput;
|
|
1054
|
+
expect(workflowInput.sandbox).toEqual({
|
|
1055
|
+
mode: "continue",
|
|
1056
|
+
sandboxId: "child-sb-1",
|
|
1057
|
+
});
|
|
1058
|
+
expect(workflowInput.sandboxShutdown).toBe("pause");
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
// --- own + init: once + continuation: fork ---
|
|
1062
|
+
|
|
1063
|
+
it("stores sandbox on first call and forks from it on second call (init=once, continuation=fork)", async () => {
|
|
1064
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1065
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1066
|
+
|
|
1067
|
+
nextStartChildResult = () => ({
|
|
1068
|
+
toolResponse: "first",
|
|
1069
|
+
data: null,
|
|
1070
|
+
threadId: "child-t-1",
|
|
1071
|
+
sandboxId: "persistent-sb",
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
const config: SubagentConfig = {
|
|
1075
|
+
agentName: "lazy-fork",
|
|
1076
|
+
description: "Lazy fork",
|
|
1077
|
+
workflow: mockWorkflow(),
|
|
1078
|
+
sandbox: { source: "own", init: "once", continuation: "fork" },
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
const { handler } = createSubagentHandler([config]);
|
|
1082
|
+
|
|
1083
|
+
await handler(
|
|
1084
|
+
{ subagent: "lazy-fork", description: "test", prompt: "first" },
|
|
1085
|
+
{ threadId: "t", toolCallId: "tc-1", toolName: "Subagent" }
|
|
1086
|
+
);
|
|
1087
|
+
|
|
1088
|
+
// First call: no sandbox init (child creates fresh), forced pause-until-parent-close
|
|
1089
|
+
const firstCall = startMock.mock.calls.at(-1);
|
|
1090
|
+
if (!firstCall) throw new Error("expected startChild call");
|
|
1091
|
+
const firstInput = firstCall[1].args[1] as SubagentWorkflowInput;
|
|
1092
|
+
expect(firstInput.sandbox).toBeUndefined();
|
|
1093
|
+
expect(firstInput.sandboxShutdown).toBe("pause-until-parent-close");
|
|
1094
|
+
|
|
1095
|
+
nextStartChildResult = () => ({
|
|
1096
|
+
toolResponse: "second",
|
|
1097
|
+
data: null,
|
|
1098
|
+
threadId: "child-t-2",
|
|
1099
|
+
sandboxId: "forked-sb",
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
// Second call WITHOUT threadId — should still fork from persistent sandbox
|
|
1103
|
+
await handler(
|
|
1104
|
+
{ subagent: "lazy-fork", description: "test", prompt: "second" },
|
|
1105
|
+
{ threadId: "t", toolCallId: "tc-2", toolName: "Subagent" }
|
|
1106
|
+
);
|
|
1107
|
+
|
|
1108
|
+
const secondCall = startMock.mock.calls.at(-1);
|
|
1109
|
+
if (!secondCall) throw new Error("expected startChild call");
|
|
1110
|
+
const secondInput = secondCall[1].args[1] as SubagentWorkflowInput;
|
|
1111
|
+
expect(secondInput.sandbox).toEqual({
|
|
1112
|
+
mode: "fork",
|
|
1113
|
+
sandboxId: "persistent-sb",
|
|
1114
|
+
});
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
// --- own + init: once + continuation: continue ---
|
|
1118
|
+
|
|
1119
|
+
it("stores sandbox on first call and continues it on second call (init=once, continuation=continue)", async () => {
|
|
1120
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1121
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1122
|
+
|
|
1123
|
+
nextStartChildResult = () => ({
|
|
1124
|
+
toolResponse: "first",
|
|
1125
|
+
data: null,
|
|
1126
|
+
threadId: "child-t-1",
|
|
1127
|
+
sandboxId: "persistent-sb",
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
const config: SubagentConfig = {
|
|
1131
|
+
agentName: "lazy-cont",
|
|
1132
|
+
description: "Lazy continue",
|
|
1133
|
+
workflow: mockWorkflow(),
|
|
1134
|
+
sandbox: { source: "own", init: "once", continuation: "continue" },
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
const { handler } = createSubagentHandler([config]);
|
|
1138
|
+
|
|
1139
|
+
await handler(
|
|
1140
|
+
{ subagent: "lazy-cont", description: "test", prompt: "first" },
|
|
1141
|
+
{ threadId: "t", toolCallId: "tc-1", toolName: "Subagent" }
|
|
1142
|
+
);
|
|
1143
|
+
|
|
1144
|
+
nextStartChildResult = () => ({
|
|
1145
|
+
toolResponse: "second",
|
|
1146
|
+
data: null,
|
|
1147
|
+
threadId: "child-t-2",
|
|
1148
|
+
sandboxId: "persistent-sb",
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
await handler(
|
|
1152
|
+
{ subagent: "lazy-cont", description: "test", prompt: "second" },
|
|
1153
|
+
{ threadId: "t", toolCallId: "tc-2", toolName: "Subagent" }
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1156
|
+
const secondCall = startMock.mock.calls.at(-1);
|
|
1157
|
+
if (!secondCall) throw new Error("expected startChild call");
|
|
1158
|
+
const secondInput = secondCall[1].args[1] as SubagentWorkflowInput;
|
|
1159
|
+
expect(secondInput.sandbox).toEqual({
|
|
1160
|
+
mode: "continue",
|
|
1161
|
+
sandboxId: "persistent-sb",
|
|
1162
|
+
});
|
|
1163
|
+
expect(secondInput.sandboxShutdown).toBe("pause");
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
// --- init: once cleanup ---
|
|
1167
|
+
|
|
1168
|
+
it("adds first-call child handle to pendingDestroys for init=once", async () => {
|
|
1169
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1170
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1171
|
+
|
|
1172
|
+
nextStartChildResult = () => ({
|
|
1173
|
+
toolResponse: "done",
|
|
1174
|
+
data: null,
|
|
1175
|
+
threadId: "child-t-1",
|
|
1176
|
+
sandboxId: "persistent-sb",
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
const config: SubagentConfig = {
|
|
1180
|
+
agentName: "lazy-cleanup",
|
|
1181
|
+
description: "Lazy cleanup",
|
|
1182
|
+
workflow: mockWorkflow(),
|
|
1183
|
+
sandbox: { source: "own", init: "once", continuation: "fork" },
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
const { handler, destroySubagentSandboxes } = createSubagentHandler([
|
|
1187
|
+
config,
|
|
1188
|
+
]);
|
|
1189
|
+
|
|
1190
|
+
await handler(
|
|
1191
|
+
{ subagent: "lazy-cleanup", description: "test", prompt: "run" },
|
|
1192
|
+
{ threadId: "t", toolCallId: "tc", toolName: "Subagent" }
|
|
1193
|
+
);
|
|
1194
|
+
|
|
1195
|
+
await destroySubagentSandboxes();
|
|
1196
|
+
|
|
1197
|
+
const lastResult = startMock.mock.results.at(-1);
|
|
1198
|
+
if (!lastResult) throw new Error("expected startChild call");
|
|
1199
|
+
const childHandle = await lastResult.value;
|
|
1200
|
+
expect(childHandle.signal).toHaveBeenCalled();
|
|
1201
|
+
});
|
|
1202
|
+
|
|
964
1203
|
it("returns sandboxId in response when child creates a sandbox", async () => {
|
|
965
1204
|
nextStartChildResult = () => ({
|
|
966
1205
|
toolResponse: "done",
|
|
@@ -973,7 +1212,7 @@ describe("createSubagentHandler", () => {
|
|
|
973
1212
|
agentName: "own-agent",
|
|
974
1213
|
description: "Own sandbox",
|
|
975
1214
|
workflow: mockWorkflow(),
|
|
976
|
-
sandbox: "own",
|
|
1215
|
+
sandbox: { source: "own", continuation: "fork" },
|
|
977
1216
|
};
|
|
978
1217
|
|
|
979
1218
|
const { handler } = createSubagentHandler([ownSubagent]);
|
|
@@ -1083,6 +1322,208 @@ describe("createSubagentHandler", () => {
|
|
|
1083
1322
|
|
|
1084
1323
|
expect(result.metadata).toBeUndefined();
|
|
1085
1324
|
});
|
|
1325
|
+
|
|
1326
|
+
// --- keep-until-parent-close ---
|
|
1327
|
+
|
|
1328
|
+
it("signals destroy for sandbox=own with keep-until-parent-close shutdown", async () => {
|
|
1329
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1330
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1331
|
+
|
|
1332
|
+
const ownSubagent: SubagentConfig = {
|
|
1333
|
+
agentName: "own-keep",
|
|
1334
|
+
description: "Own sandbox kept",
|
|
1335
|
+
workflow: mockWorkflow(),
|
|
1336
|
+
sandbox: {
|
|
1337
|
+
source: "own",
|
|
1338
|
+
continuation: "fork",
|
|
1339
|
+
shutdown: "keep-until-parent-close",
|
|
1340
|
+
},
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
const { handler, destroySubagentSandboxes } = createSubagentHandler([
|
|
1344
|
+
ownSubagent,
|
|
1345
|
+
]);
|
|
1346
|
+
|
|
1347
|
+
await handler(
|
|
1348
|
+
{ subagent: "own-keep", description: "test", prompt: "run" },
|
|
1349
|
+
{ threadId: "t", toolCallId: "tc", toolName: "Subagent" }
|
|
1350
|
+
);
|
|
1351
|
+
|
|
1352
|
+
await destroySubagentSandboxes();
|
|
1353
|
+
|
|
1354
|
+
const lastResult = startMock.mock.results.at(-1);
|
|
1355
|
+
if (!lastResult) throw new Error("expected startChild call");
|
|
1356
|
+
const childHandle = await lastResult.value;
|
|
1357
|
+
expect(childHandle.signal).toHaveBeenCalled();
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
it("does not signal destroy for sandbox=own with keep shutdown (without parent-close)", async () => {
|
|
1361
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1362
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1363
|
+
|
|
1364
|
+
const ownSubagent: SubagentConfig = {
|
|
1365
|
+
agentName: "own-keep-plain",
|
|
1366
|
+
description: "Own sandbox keep",
|
|
1367
|
+
workflow: mockWorkflow(),
|
|
1368
|
+
sandbox: { source: "own", continuation: "fork", shutdown: "keep" },
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
const { handler, destroySubagentSandboxes } = createSubagentHandler([
|
|
1372
|
+
ownSubagent,
|
|
1373
|
+
]);
|
|
1374
|
+
|
|
1375
|
+
await handler(
|
|
1376
|
+
{ subagent: "own-keep-plain", description: "test", prompt: "run" },
|
|
1377
|
+
{ threadId: "t", toolCallId: "tc", toolName: "Subagent" }
|
|
1378
|
+
);
|
|
1379
|
+
|
|
1380
|
+
const lastResult = startMock.mock.results.at(-1);
|
|
1381
|
+
if (!lastResult) throw new Error("expected startChild call");
|
|
1382
|
+
const childHandle = await lastResult.value;
|
|
1383
|
+
childHandle.signal.mockClear();
|
|
1384
|
+
|
|
1385
|
+
await destroySubagentSandboxes();
|
|
1386
|
+
|
|
1387
|
+
expect(childHandle.signal).not.toHaveBeenCalled();
|
|
1388
|
+
});
|
|
1389
|
+
|
|
1390
|
+
// --- mustSurvive does not override user shutdown ---
|
|
1391
|
+
|
|
1392
|
+
it("does not override keep-until-parent-close with pause-until-parent-close for init=once", async () => {
|
|
1393
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1394
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1395
|
+
|
|
1396
|
+
nextStartChildResult = () => ({
|
|
1397
|
+
toolResponse: "first",
|
|
1398
|
+
data: null,
|
|
1399
|
+
threadId: "child-t-1",
|
|
1400
|
+
sandboxId: "persistent-sb",
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
const config: SubagentConfig = {
|
|
1404
|
+
agentName: "lazy-keep",
|
|
1405
|
+
description: "Lazy keep",
|
|
1406
|
+
workflow: mockWorkflow(),
|
|
1407
|
+
sandbox: {
|
|
1408
|
+
source: "own",
|
|
1409
|
+
init: "once",
|
|
1410
|
+
continuation: "fork",
|
|
1411
|
+
shutdown: "keep-until-parent-close",
|
|
1412
|
+
},
|
|
1413
|
+
};
|
|
1414
|
+
|
|
1415
|
+
const { handler } = createSubagentHandler([config]);
|
|
1416
|
+
|
|
1417
|
+
await handler(
|
|
1418
|
+
{ subagent: "lazy-keep", description: "test", prompt: "first" },
|
|
1419
|
+
{ threadId: "t", toolCallId: "tc-1", toolName: "Subagent" }
|
|
1420
|
+
);
|
|
1421
|
+
|
|
1422
|
+
const firstCall = startMock.mock.calls.at(-1);
|
|
1423
|
+
if (!firstCall) throw new Error("expected startChild call");
|
|
1424
|
+
const firstInput = firstCall[1].args[1] as SubagentWorkflowInput;
|
|
1425
|
+
expect(firstInput.sandboxShutdown).toBe("keep-until-parent-close");
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
it("does not override pause with pause-until-parent-close for init=once", async () => {
|
|
1429
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1430
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1431
|
+
|
|
1432
|
+
nextStartChildResult = () => ({
|
|
1433
|
+
toolResponse: "first",
|
|
1434
|
+
data: null,
|
|
1435
|
+
threadId: "child-t-1",
|
|
1436
|
+
sandboxId: "persistent-sb",
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
const config: SubagentConfig = {
|
|
1440
|
+
agentName: "lazy-pause",
|
|
1441
|
+
description: "Lazy pause",
|
|
1442
|
+
workflow: mockWorkflow(),
|
|
1443
|
+
sandbox: {
|
|
1444
|
+
source: "own",
|
|
1445
|
+
init: "once",
|
|
1446
|
+
continuation: "fork",
|
|
1447
|
+
shutdown: "pause",
|
|
1448
|
+
},
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
const { handler } = createSubagentHandler([config]);
|
|
1452
|
+
|
|
1453
|
+
await handler(
|
|
1454
|
+
{ subagent: "lazy-pause", description: "test", prompt: "first" },
|
|
1455
|
+
{ threadId: "t", toolCallId: "tc-1", toolName: "Subagent" }
|
|
1456
|
+
);
|
|
1457
|
+
|
|
1458
|
+
const firstCall = startMock.mock.calls.at(-1);
|
|
1459
|
+
if (!firstCall) throw new Error("expected startChild call");
|
|
1460
|
+
const firstInput = firstCall[1].args[1] as SubagentWorkflowInput;
|
|
1461
|
+
expect(firstInput.sandboxShutdown).toBe("pause");
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
it("does not override keep with pause for continuation=continue", async () => {
|
|
1465
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1466
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1467
|
+
|
|
1468
|
+
nextStartChildResult = () => ({
|
|
1469
|
+
toolResponse: "first",
|
|
1470
|
+
data: null,
|
|
1471
|
+
threadId: "child-t-1",
|
|
1472
|
+
sandboxId: "child-sb-1",
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1475
|
+
const config: SubagentConfig = {
|
|
1476
|
+
agentName: "cont-keep",
|
|
1477
|
+
description: "Continue keep",
|
|
1478
|
+
workflow: mockWorkflow(),
|
|
1479
|
+
thread: "continue",
|
|
1480
|
+
sandbox: { source: "own", continuation: "continue", shutdown: "keep" },
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
const { handler } = createSubagentHandler([config]);
|
|
1484
|
+
|
|
1485
|
+
await handler(
|
|
1486
|
+
{ subagent: "cont-keep", description: "test", prompt: "first" },
|
|
1487
|
+
{ threadId: "t", toolCallId: "tc-1", toolName: "Subagent" }
|
|
1488
|
+
);
|
|
1489
|
+
|
|
1490
|
+
const firstCall = startMock.mock.calls.at(-1);
|
|
1491
|
+
if (!firstCall) throw new Error("expected startChild call");
|
|
1492
|
+
const firstInput = firstCall[1].args[1] as SubagentWorkflowInput;
|
|
1493
|
+
expect(firstInput.sandboxShutdown).toBe("keep");
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
it("still overrides destroy with pause for continuation=continue", async () => {
|
|
1497
|
+
const { startChild } = await import("@temporalio/workflow");
|
|
1498
|
+
const startMock = startChild as ReturnType<typeof vi.fn>;
|
|
1499
|
+
|
|
1500
|
+
nextStartChildResult = () => ({
|
|
1501
|
+
toolResponse: "first",
|
|
1502
|
+
data: null,
|
|
1503
|
+
threadId: "child-t-1",
|
|
1504
|
+
sandboxId: "child-sb-1",
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
const config: SubagentConfig = {
|
|
1508
|
+
agentName: "cont-destroy",
|
|
1509
|
+
description: "Continue destroy",
|
|
1510
|
+
workflow: mockWorkflow(),
|
|
1511
|
+
thread: "continue",
|
|
1512
|
+
sandbox: { source: "own", continuation: "continue", shutdown: "destroy" },
|
|
1513
|
+
};
|
|
1514
|
+
|
|
1515
|
+
const { handler } = createSubagentHandler([config]);
|
|
1516
|
+
|
|
1517
|
+
await handler(
|
|
1518
|
+
{ subagent: "cont-destroy", description: "test", prompt: "first" },
|
|
1519
|
+
{ threadId: "t", toolCallId: "tc-1", toolName: "Subagent" }
|
|
1520
|
+
);
|
|
1521
|
+
|
|
1522
|
+
const firstCall = startMock.mock.calls.at(-1);
|
|
1523
|
+
if (!firstCall) throw new Error("expected startChild call");
|
|
1524
|
+
const firstInput = firstCall[1].args[1] as SubagentWorkflowInput;
|
|
1525
|
+
expect(firstInput.sandboxShutdown).toBe("pause");
|
|
1526
|
+
});
|
|
1086
1527
|
});
|
|
1087
1528
|
|
|
1088
1529
|
// ---------------------------------------------------------------------------
|
|
@@ -1289,6 +1730,7 @@ describe("defineSubagentWorkflow", () => {
|
|
|
1289
1730
|
agentName: "test",
|
|
1290
1731
|
sandboxShutdown: "destroy",
|
|
1291
1732
|
thread: { mode: "fork", threadId: "prev-42" },
|
|
1733
|
+
onSandboxReady: expect.any(Function),
|
|
1292
1734
|
});
|
|
1293
1735
|
});
|
|
1294
1736
|
|
|
@@ -1307,6 +1749,7 @@ describe("defineSubagentWorkflow", () => {
|
|
|
1307
1749
|
agentName: "test",
|
|
1308
1750
|
sandboxShutdown: "destroy",
|
|
1309
1751
|
sandbox: { mode: "inherit", sandboxId: "sb-123" },
|
|
1752
|
+
onSandboxReady: expect.any(Function),
|
|
1310
1753
|
});
|
|
1311
1754
|
});
|
|
1312
1755
|
|
|
@@ -1325,6 +1768,7 @@ describe("defineSubagentWorkflow", () => {
|
|
|
1325
1768
|
agentName: "test",
|
|
1326
1769
|
sandboxShutdown: "destroy",
|
|
1327
1770
|
sandbox: { mode: "fork", sandboxId: "prev-sb-1" },
|
|
1771
|
+
onSandboxReady: expect.any(Function),
|
|
1328
1772
|
});
|
|
1329
1773
|
});
|
|
1330
1774
|
|
|
@@ -1347,6 +1791,7 @@ describe("defineSubagentWorkflow", () => {
|
|
|
1347
1791
|
sandboxShutdown: "destroy",
|
|
1348
1792
|
thread: { mode: "fork", threadId: "prev-t" },
|
|
1349
1793
|
sandbox: { mode: "fork", sandboxId: "prev-sb" },
|
|
1794
|
+
onSandboxReady: expect.any(Function),
|
|
1350
1795
|
});
|
|
1351
1796
|
});
|
|
1352
1797
|
|
|
@@ -1416,6 +1861,68 @@ describe("defineSubagentWorkflow", () => {
|
|
|
1416
1861
|
expect(capturedSession).toEqual({
|
|
1417
1862
|
agentName: "test",
|
|
1418
1863
|
sandboxShutdown: "destroy",
|
|
1864
|
+
onSandboxReady: expect.any(Function),
|
|
1419
1865
|
});
|
|
1420
1866
|
});
|
|
1867
|
+
|
|
1868
|
+
it("validates destroySandbox required for keep-until-parent-close", async () => {
|
|
1869
|
+
// @ts-expect-error — deliberately omitting destroySandbox to test runtime validation
|
|
1870
|
+
const workflow = defineSubagentWorkflow(
|
|
1871
|
+
{
|
|
1872
|
+
name: "test",
|
|
1873
|
+
description: "test agent",
|
|
1874
|
+
sandboxShutdown: "keep-until-parent-close",
|
|
1875
|
+
},
|
|
1876
|
+
async () => ({
|
|
1877
|
+
toolResponse: "ok",
|
|
1878
|
+
data: null,
|
|
1879
|
+
threadId: "t",
|
|
1880
|
+
sandboxId: "sb-1",
|
|
1881
|
+
})
|
|
1882
|
+
);
|
|
1883
|
+
|
|
1884
|
+
await expect(workflow("go", {})).rejects.toThrow(
|
|
1885
|
+
/keep-until-parent-close.*destroySandbox/
|
|
1886
|
+
);
|
|
1887
|
+
});
|
|
1888
|
+
|
|
1889
|
+
it("validates sandboxId required for keep-until-parent-close", async () => {
|
|
1890
|
+
// @ts-expect-error — deliberately omitting sandboxId to test runtime validation
|
|
1891
|
+
const workflow = defineSubagentWorkflow(
|
|
1892
|
+
{
|
|
1893
|
+
name: "test",
|
|
1894
|
+
description: "test agent",
|
|
1895
|
+
sandboxShutdown: "keep-until-parent-close",
|
|
1896
|
+
},
|
|
1897
|
+
async () => ({
|
|
1898
|
+
toolResponse: "ok",
|
|
1899
|
+
data: null,
|
|
1900
|
+
threadId: "t",
|
|
1901
|
+
destroySandbox: async () => {},
|
|
1902
|
+
})
|
|
1903
|
+
);
|
|
1904
|
+
|
|
1905
|
+
await expect(workflow("go", {})).rejects.toThrow(
|
|
1906
|
+
/keep-until-parent-close.*sandboxId/
|
|
1907
|
+
);
|
|
1908
|
+
});
|
|
1909
|
+
|
|
1910
|
+
it("uses keep-until-parent-close from workflowInput override in sessionInput", async () => {
|
|
1911
|
+
let capturedSession: SubagentSessionInput | undefined;
|
|
1912
|
+
const workflow = defineSubagentWorkflow(
|
|
1913
|
+
{ name: "test", description: "test agent" },
|
|
1914
|
+
async (_prompt, sessionInput) => {
|
|
1915
|
+
capturedSession = sessionInput;
|
|
1916
|
+
return { toolResponse: "ok", data: null, threadId: "t" };
|
|
1917
|
+
}
|
|
1918
|
+
);
|
|
1919
|
+
|
|
1920
|
+
// Validation will throw because destroySandbox is missing, but sessionInput is captured first
|
|
1921
|
+
try {
|
|
1922
|
+
await workflow("go", { sandboxShutdown: "keep-until-parent-close" });
|
|
1923
|
+
} catch {
|
|
1924
|
+
// expected — no destroySandbox callback
|
|
1925
|
+
}
|
|
1926
|
+
expect(capturedSession?.sandboxShutdown).toBe("keep-until-parent-close");
|
|
1927
|
+
});
|
|
1421
1928
|
});
|