zeitlich 0.2.38 → 0.2.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/{activities-BKhMtKDd.d.ts → activities-CULxRzJ1.d.ts} +4 -6
- package/dist/{activities-CDcwkRZs.d.cts → activities-CvUrG3YG.d.cts} +4 -6
- package/dist/adapter-id-BB-mmrts.d.cts +17 -0
- package/dist/adapter-id-BB-mmrts.d.ts +17 -0
- package/dist/adapter-id-CMwVrVqv.d.cts +17 -0
- package/dist/adapter-id-CMwVrVqv.d.ts +17 -0
- package/dist/adapter-id-CbY2zeSt.d.cts +17 -0
- package/dist/adapter-id-CbY2zeSt.d.ts +17 -0
- package/dist/adapters/thread/anthropic/index.cjs +140 -23
- package/dist/adapters/thread/anthropic/index.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/index.d.cts +8 -7
- package/dist/adapters/thread/anthropic/index.d.ts +8 -7
- package/dist/adapters/thread/anthropic/index.js +140 -24
- package/dist/adapters/thread/anthropic/index.js.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.cjs +8 -3
- package/dist/adapters/thread/anthropic/workflow.cjs.map +1 -1
- package/dist/adapters/thread/anthropic/workflow.d.cts +5 -4
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -4
- package/dist/adapters/thread/anthropic/workflow.js +8 -4
- package/dist/adapters/thread/anthropic/workflow.js.map +1 -1
- package/dist/adapters/thread/google-genai/index.cjs +140 -23
- package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/index.d.cts +5 -4
- package/dist/adapters/thread/google-genai/index.d.ts +5 -4
- package/dist/adapters/thread/google-genai/index.js +140 -24
- package/dist/adapters/thread/google-genai/index.js.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.cjs +8 -3
- package/dist/adapters/thread/google-genai/workflow.cjs.map +1 -1
- package/dist/adapters/thread/google-genai/workflow.d.cts +5 -4
- package/dist/adapters/thread/google-genai/workflow.d.ts +5 -4
- package/dist/adapters/thread/google-genai/workflow.js +8 -4
- package/dist/adapters/thread/google-genai/workflow.js.map +1 -1
- package/dist/adapters/thread/index.cjs +16 -0
- package/dist/adapters/thread/index.cjs.map +1 -0
- package/dist/adapters/thread/index.d.cts +34 -0
- package/dist/adapters/thread/index.d.ts +34 -0
- package/dist/adapters/thread/index.js +12 -0
- package/dist/adapters/thread/index.js.map +1 -0
- package/dist/adapters/thread/langchain/index.cjs +139 -24
- package/dist/adapters/thread/langchain/index.cjs.map +1 -1
- package/dist/adapters/thread/langchain/index.d.cts +8 -7
- package/dist/adapters/thread/langchain/index.d.ts +8 -7
- package/dist/adapters/thread/langchain/index.js +139 -25
- package/dist/adapters/thread/langchain/index.js.map +1 -1
- package/dist/adapters/thread/langchain/workflow.cjs +8 -3
- package/dist/adapters/thread/langchain/workflow.cjs.map +1 -1
- package/dist/adapters/thread/langchain/workflow.d.cts +5 -4
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -4
- package/dist/adapters/thread/langchain/workflow.js +8 -4
- package/dist/adapters/thread/langchain/workflow.js.map +1 -1
- package/dist/index.cjs +267 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +264 -49
- package/dist/index.js.map +1 -1
- package/dist/{proxy-D_3x7RN4.d.cts → proxy-5EbwzaY4.d.cts} +1 -1
- package/dist/{proxy-CUlKSvZS.d.ts → proxy-wZufFfBh.d.ts} +1 -1
- package/dist/{thread-manager-CVu7o2cs.d.ts → thread-manager-BNiIt5r8.d.ts} +2 -4
- package/dist/{thread-manager-c1gPopAG.d.ts → thread-manager-BoN5DOvG.d.cts} +2 -4
- package/dist/{thread-manager-wGi-LqIP.d.cts → thread-manager-BqBAIsED.d.ts} +2 -4
- package/dist/{thread-manager-HSwyh28L.d.cts → thread-manager-DF8WuCRs.d.cts} +2 -4
- package/dist/{types-BH_IRryz.d.ts → types-C7OoY7h8.d.ts} +54 -6
- package/dist/{types-C06FwR96.d.cts → types-Cn2r3ol3.d.cts} +163 -44
- package/dist/{types-BaOw4hKI.d.cts → types-CuISs0Ub.d.cts} +54 -6
- package/dist/{types-DNr31FzL.d.ts → types-DeQH84C_.d.ts} +163 -44
- package/dist/{workflow-CSCkpwAL.d.ts → workflow-C2MZZj5K.d.ts} +82 -2
- package/dist/{workflow-DuvMZ8Vm.d.cts → workflow-DhplIN65.d.cts} +82 -2
- package/dist/workflow.cjs +189 -37
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +2 -2
- package/dist/workflow.d.ts +2 -2
- package/dist/workflow.js +186 -38
- package/dist/workflow.js.map +1 -1
- package/package.json +11 -1
- package/src/adapters/thread/adapter-id.test.ts +42 -0
- package/src/adapters/thread/anthropic/activities.ts +33 -7
- package/src/adapters/thread/anthropic/adapter-id.ts +16 -0
- package/src/adapters/thread/anthropic/fork-transform.test.ts +291 -0
- package/src/adapters/thread/anthropic/index.ts +3 -0
- package/src/adapters/thread/anthropic/model-invoker.ts +8 -4
- package/src/adapters/thread/anthropic/proxy.ts +3 -2
- package/src/adapters/thread/anthropic/thread-manager.ts +27 -4
- package/src/adapters/thread/google-genai/activities.ts +33 -7
- package/src/adapters/thread/google-genai/adapter-id.ts +16 -0
- package/src/adapters/thread/google-genai/fork-transform.test.ts +149 -0
- package/src/adapters/thread/google-genai/index.ts +3 -0
- package/src/adapters/thread/google-genai/model-invoker.ts +7 -3
- package/src/adapters/thread/google-genai/proxy.ts +3 -2
- package/src/adapters/thread/google-genai/thread-manager.ts +27 -4
- package/src/adapters/thread/index.ts +39 -0
- package/src/adapters/thread/langchain/activities.ts +33 -7
- package/src/adapters/thread/langchain/adapter-id.ts +16 -0
- package/src/adapters/thread/langchain/fork-transform.test.ts +142 -0
- package/src/adapters/thread/langchain/index.ts +3 -0
- package/src/adapters/thread/langchain/model-invoker.ts +8 -3
- package/src/adapters/thread/langchain/proxy.ts +3 -2
- package/src/adapters/thread/langchain/thread-manager.ts +27 -4
- package/src/lib/lifecycle.ts +3 -1
- package/src/lib/model/types.ts +7 -10
- package/src/lib/session/session-edge-cases.integration.test.ts +131 -63
- package/src/lib/session/session.integration.test.ts +174 -5
- package/src/lib/session/session.ts +69 -28
- package/src/lib/session/types.ts +61 -9
- package/src/lib/state/index.ts +1 -0
- package/src/lib/state/manager.integration.test.ts +109 -0
- package/src/lib/state/manager.ts +38 -8
- package/src/lib/state/types.ts +25 -0
- package/src/lib/subagent/handler.ts +124 -11
- package/src/lib/subagent/index.ts +5 -1
- package/src/lib/subagent/subagent.integration.test.ts +528 -0
- package/src/lib/subagent/types.ts +63 -14
- package/src/lib/subagent/workflow.ts +29 -2
- package/src/lib/thread/index.ts +5 -0
- package/src/lib/thread/keys.test.ts +101 -0
- package/src/lib/thread/keys.ts +94 -0
- package/src/lib/thread/manager.test.ts +139 -0
- package/src/lib/thread/manager.ts +92 -14
- package/src/lib/thread/proxy.ts +2 -0
- package/src/lib/thread/types.ts +60 -6
- package/src/lib/tool-router/types.ts +16 -8
- package/src/lib/types.ts +12 -0
- package/src/workflow.ts +12 -1
- package/tsup.config.ts +1 -0
package/dist/index.cjs
CHANGED
|
@@ -444,6 +444,7 @@ function createSubagentTool(subagents) {
|
|
|
444
444
|
var childSandboxReadySignal = workflow.defineSignal("childSandboxReady");
|
|
445
445
|
|
|
446
446
|
// src/lib/subagent/handler.ts
|
|
447
|
+
var DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = "1h";
|
|
447
448
|
function resolveSandboxConfig(config) {
|
|
448
449
|
if (!config || config === "none") {
|
|
449
450
|
return { source: "none", init: "per-call", continuation: "fork" };
|
|
@@ -475,17 +476,28 @@ function createSubagentHandler(subagents) {
|
|
|
475
476
|
const threadSandboxes = /* @__PURE__ */ new Map();
|
|
476
477
|
const persistentSandboxes = /* @__PURE__ */ new Map();
|
|
477
478
|
const persistentSandboxCreating = /* @__PURE__ */ new Set();
|
|
479
|
+
const persistentSandboxCreationError = /* @__PURE__ */ new Map();
|
|
478
480
|
const lazyCreatorAgent = /* @__PURE__ */ new Map();
|
|
481
|
+
const snapshotBaseCreatorAgent = /* @__PURE__ */ new Map();
|
|
479
482
|
const threadSnapshots = /* @__PURE__ */ new Map();
|
|
480
483
|
const persistentBaseSnapshot = /* @__PURE__ */ new Map();
|
|
481
484
|
const persistentBaseSnapshotCreating = /* @__PURE__ */ new Set();
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
lazyCreatorAgent.
|
|
485
|
+
const persistentBaseSnapshotCreationError = /* @__PURE__ */ new Map();
|
|
486
|
+
workflow.setHandler(
|
|
487
|
+
childSandboxReadySignal,
|
|
488
|
+
({ childWorkflowId, sandboxId, baseSnapshot }) => {
|
|
489
|
+
const lazyAgent = lazyCreatorAgent.get(childWorkflowId);
|
|
490
|
+
if (lazyAgent && !persistentSandboxes.has(lazyAgent)) {
|
|
491
|
+
persistentSandboxes.set(lazyAgent, sandboxId);
|
|
492
|
+
lazyCreatorAgent.delete(childWorkflowId);
|
|
493
|
+
}
|
|
494
|
+
const snapAgent = snapshotBaseCreatorAgent.get(childWorkflowId);
|
|
495
|
+
if (snapAgent && baseSnapshot && !persistentBaseSnapshot.has(snapAgent)) {
|
|
496
|
+
persistentBaseSnapshot.set(snapAgent, baseSnapshot);
|
|
497
|
+
snapshotBaseCreatorAgent.delete(childWorkflowId);
|
|
498
|
+
}
|
|
487
499
|
}
|
|
488
|
-
|
|
500
|
+
);
|
|
489
501
|
const handler = async (args, context) => {
|
|
490
502
|
const config = subagents.find((s) => s.agentName === args.subagent);
|
|
491
503
|
if (!config) {
|
|
@@ -541,8 +553,20 @@ function createSubagentHandler(subagents) {
|
|
|
541
553
|
baseSnap = persistentBaseSnapshot.get(config.agentName);
|
|
542
554
|
if (!baseSnap) {
|
|
543
555
|
if (persistentBaseSnapshotCreating.has(config.agentName)) {
|
|
544
|
-
await workflow.condition(
|
|
556
|
+
await workflow.condition(
|
|
557
|
+
() => persistentBaseSnapshot.has(config.agentName) || persistentBaseSnapshotCreationError.has(config.agentName) || !persistentBaseSnapshotCreating.has(config.agentName)
|
|
558
|
+
);
|
|
559
|
+
const creatorErr = persistentBaseSnapshotCreationError.get(
|
|
560
|
+
config.agentName
|
|
561
|
+
);
|
|
562
|
+
if (creatorErr !== void 0) {
|
|
563
|
+
throw creatorErr;
|
|
564
|
+
}
|
|
545
565
|
baseSnap = persistentBaseSnapshot.get(config.agentName);
|
|
566
|
+
if (!baseSnap) {
|
|
567
|
+
persistentBaseSnapshotCreating.add(config.agentName);
|
|
568
|
+
isSnapshotBaseCreator = true;
|
|
569
|
+
}
|
|
546
570
|
} else {
|
|
547
571
|
persistentBaseSnapshotCreating.add(config.agentName);
|
|
548
572
|
isSnapshotBaseCreator = true;
|
|
@@ -560,8 +584,20 @@ function createSubagentHandler(subagents) {
|
|
|
560
584
|
baseSandboxId = persistentSandboxes.get(config.agentName);
|
|
561
585
|
if (!baseSandboxId) {
|
|
562
586
|
if (persistentSandboxCreating.has(config.agentName)) {
|
|
563
|
-
await workflow.condition(
|
|
587
|
+
await workflow.condition(
|
|
588
|
+
() => persistentSandboxes.has(config.agentName) || persistentSandboxCreationError.has(config.agentName) || !persistentSandboxCreating.has(config.agentName)
|
|
589
|
+
);
|
|
590
|
+
const creatorErr = persistentSandboxCreationError.get(
|
|
591
|
+
config.agentName
|
|
592
|
+
);
|
|
593
|
+
if (creatorErr !== void 0) {
|
|
594
|
+
throw creatorErr;
|
|
595
|
+
}
|
|
564
596
|
baseSandboxId = persistentSandboxes.get(config.agentName);
|
|
597
|
+
if (!baseSandboxId) {
|
|
598
|
+
persistentSandboxCreating.add(config.agentName);
|
|
599
|
+
isLazyCreator = true;
|
|
600
|
+
}
|
|
565
601
|
} else {
|
|
566
602
|
persistentSandboxCreating.add(config.agentName);
|
|
567
603
|
isLazyCreator = true;
|
|
@@ -590,6 +626,12 @@ function createSubagentHandler(subagents) {
|
|
|
590
626
|
};
|
|
591
627
|
const resolvedContext = config.context === void 0 ? void 0 : typeof config.context === "function" ? config.context() : config.context;
|
|
592
628
|
const childOpts = {
|
|
629
|
+
// Apply a bounded run timeout by default so a child workflow that
|
|
630
|
+
// fails to initialize or otherwise never reaches a terminal state
|
|
631
|
+
// cannot hang the parent's `Subagent` tool call forever. Callers can
|
|
632
|
+
// raise, lower, or disable it via `workflowOptions.workflowRunTimeout`.
|
|
633
|
+
workflowRunTimeout: DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT,
|
|
634
|
+
...config.workflowOptions ?? {},
|
|
593
635
|
workflowId: childWorkflowId,
|
|
594
636
|
args: resolvedContext === void 0 ? [args.prompt, workflowInput] : [args.prompt, workflowInput, resolvedContext],
|
|
595
637
|
taskQueue: config.taskQueue ?? parentTaskQueue
|
|
@@ -597,13 +639,39 @@ function createSubagentHandler(subagents) {
|
|
|
597
639
|
if (isLazyCreator) {
|
|
598
640
|
lazyCreatorAgent.set(childWorkflowId, config.agentName);
|
|
599
641
|
}
|
|
642
|
+
if (isSnapshotBaseCreator) {
|
|
643
|
+
snapshotBaseCreatorAgent.set(childWorkflowId, config.agentName);
|
|
644
|
+
}
|
|
600
645
|
workflow.log.info("subagent spawned", {
|
|
601
646
|
subagent: config.agentName,
|
|
602
647
|
childWorkflowId,
|
|
603
648
|
threadMode,
|
|
604
649
|
sandboxSource: sandboxCfg.source
|
|
605
650
|
});
|
|
606
|
-
|
|
651
|
+
let childResult;
|
|
652
|
+
try {
|
|
653
|
+
childResult = await workflow.executeChild(
|
|
654
|
+
config.workflow,
|
|
655
|
+
childOpts
|
|
656
|
+
);
|
|
657
|
+
} catch (err) {
|
|
658
|
+
workflow.log.warn("subagent failed", {
|
|
659
|
+
subagent: config.agentName,
|
|
660
|
+
childWorkflowId,
|
|
661
|
+
error: err instanceof Error ? err.message : String(err)
|
|
662
|
+
});
|
|
663
|
+
if (isLazyCreator) {
|
|
664
|
+
persistentSandboxCreating.delete(config.agentName);
|
|
665
|
+
persistentSandboxCreationError.set(config.agentName, err);
|
|
666
|
+
lazyCreatorAgent.delete(childWorkflowId);
|
|
667
|
+
}
|
|
668
|
+
if (isSnapshotBaseCreator) {
|
|
669
|
+
persistentBaseSnapshotCreating.delete(config.agentName);
|
|
670
|
+
persistentBaseSnapshotCreationError.set(config.agentName, err);
|
|
671
|
+
snapshotBaseCreatorAgent.delete(childWorkflowId);
|
|
672
|
+
}
|
|
673
|
+
throw err;
|
|
674
|
+
}
|
|
607
675
|
const effectiveShutdown = sandboxShutdownOverride ?? sandboxCfg.shutdown ?? "destroy";
|
|
608
676
|
workflow.log.info("subagent completed", {
|
|
609
677
|
subagent: config.agentName,
|
|
@@ -647,10 +715,13 @@ function createSubagentHandler(subagents) {
|
|
|
647
715
|
}
|
|
648
716
|
if (isLazyCreator) {
|
|
649
717
|
persistentSandboxCreating.delete(config.agentName);
|
|
718
|
+
persistentSandboxCreationError.delete(config.agentName);
|
|
650
719
|
lazyCreatorAgent.delete(childWorkflowId);
|
|
651
720
|
}
|
|
652
721
|
if (isSnapshotBaseCreator) {
|
|
653
722
|
persistentBaseSnapshotCreating.delete(config.agentName);
|
|
723
|
+
persistentBaseSnapshotCreationError.delete(config.agentName);
|
|
724
|
+
snapshotBaseCreatorAgent.delete(childWorkflowId);
|
|
654
725
|
}
|
|
655
726
|
if (!toolResponse) {
|
|
656
727
|
return {
|
|
@@ -893,6 +964,7 @@ async function createSession({
|
|
|
893
964
|
sandbox: sandboxInit,
|
|
894
965
|
sandboxShutdown = "destroy",
|
|
895
966
|
onSandboxReady,
|
|
967
|
+
onSessionExit,
|
|
896
968
|
virtualFs: virtualFsConfig,
|
|
897
969
|
virtualFsOps
|
|
898
970
|
}) {
|
|
@@ -918,7 +990,8 @@ async function createSession({
|
|
|
918
990
|
appendSystemMessage,
|
|
919
991
|
appendAgentMessage,
|
|
920
992
|
forkThread,
|
|
921
|
-
|
|
993
|
+
loadThreadState,
|
|
994
|
+
saveThreadState
|
|
922
995
|
} = threadOps;
|
|
923
996
|
const plugins = [];
|
|
924
997
|
let destroySubagentSandboxes;
|
|
@@ -1046,7 +1119,10 @@ async function createSession({
|
|
|
1046
1119
|
baseSnapshot = await sandboxOps.snapshotSandbox(sandboxId);
|
|
1047
1120
|
}
|
|
1048
1121
|
if (sandboxId && sandboxOwned && onSandboxReady) {
|
|
1049
|
-
onSandboxReady(
|
|
1122
|
+
onSandboxReady({
|
|
1123
|
+
sandboxId,
|
|
1124
|
+
...baseSnapshot && { baseSnapshot }
|
|
1125
|
+
});
|
|
1050
1126
|
}
|
|
1051
1127
|
if (virtualFsConfig) {
|
|
1052
1128
|
if (!virtualFsOps) {
|
|
@@ -1089,9 +1165,20 @@ async function createSession({
|
|
|
1089
1165
|
});
|
|
1090
1166
|
const sessionStartMs = Date.now();
|
|
1091
1167
|
const systemPrompt = stateManager.getSystemPrompt();
|
|
1168
|
+
const rehydrateFromSlice = (slice) => {
|
|
1169
|
+
stateManager.mergeUpdate({
|
|
1170
|
+
tasks: new Map(slice.tasks),
|
|
1171
|
+
...slice.custom
|
|
1172
|
+
});
|
|
1173
|
+
};
|
|
1092
1174
|
if (threadMode === "fork" && sourceThreadId) {
|
|
1093
1175
|
await forkThread(sourceThreadId, threadId, threadKey);
|
|
1094
|
-
|
|
1176
|
+
const forkedSlice = await loadThreadState(threadId, threadKey);
|
|
1177
|
+
if (forkedSlice) rehydrateFromSlice(forkedSlice);
|
|
1178
|
+
} else if (threadMode === "continue") {
|
|
1179
|
+
const continuedSlice = await loadThreadState(threadId, threadKey);
|
|
1180
|
+
if (continuedSlice) rehydrateFromSlice(continuedSlice);
|
|
1181
|
+
} else {
|
|
1095
1182
|
if (appendSystemPrompt) {
|
|
1096
1183
|
if (systemPrompt == null || typeof systemPrompt === "string" && systemPrompt.trim() === "") {
|
|
1097
1184
|
throw workflow.ApplicationFailure.create({
|
|
@@ -1113,24 +1200,21 @@ async function createSession({
|
|
|
1113
1200
|
let exitReason = "completed";
|
|
1114
1201
|
let finalMessage = null;
|
|
1115
1202
|
try {
|
|
1203
|
+
let assistantId;
|
|
1116
1204
|
while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
|
|
1117
1205
|
stateManager.incrementTurns();
|
|
1118
1206
|
const currentTurn = stateManager.getTurns();
|
|
1119
1207
|
workflow.log.debug("turn started", { agentName, threadId, turn: currentTurn });
|
|
1120
1208
|
stateManager.setTools(toolRouter.getToolDefinitions());
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
rawToolCalls,
|
|
1124
|
-
usage,
|
|
1125
|
-
threadLengthAtCall
|
|
1126
|
-
} = await runAgent({
|
|
1209
|
+
assistantId ??= workflow.uuid4();
|
|
1210
|
+
const { message, rawToolCalls, usage } = await runAgent({
|
|
1127
1211
|
threadId,
|
|
1128
1212
|
threadKey,
|
|
1129
1213
|
agentName,
|
|
1130
|
-
metadata
|
|
1214
|
+
metadata,
|
|
1215
|
+
assistantMessageId: assistantId
|
|
1131
1216
|
});
|
|
1132
|
-
|
|
1133
|
-
await appendAgentMessage(threadId, workflow.uuid4(), message, threadKey);
|
|
1217
|
+
await appendAgentMessage(threadId, assistantId, message, threadKey);
|
|
1134
1218
|
if (usage) {
|
|
1135
1219
|
stateManager.updateUsage(usage);
|
|
1136
1220
|
}
|
|
@@ -1184,15 +1268,9 @@ async function createSession({
|
|
|
1184
1268
|
toolCallId: rewind.toolCallId,
|
|
1185
1269
|
toolName: rewind.toolName
|
|
1186
1270
|
});
|
|
1187
|
-
if (preAssistantLength === void 0) {
|
|
1188
|
-
throw workflow.ApplicationFailure.create({
|
|
1189
|
-
message: "Rewind requested but runAgent did not report `threadLengthAtCall`; the adapter must populate it to support rewinds.",
|
|
1190
|
-
nonRetryable: true
|
|
1191
|
-
});
|
|
1192
|
-
}
|
|
1193
|
-
await truncateThread(threadId, preAssistantLength, threadKey);
|
|
1194
1271
|
continue;
|
|
1195
1272
|
}
|
|
1273
|
+
assistantId = void 0;
|
|
1196
1274
|
if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
|
|
1197
1275
|
const conditionMet = await workflow.condition(
|
|
1198
1276
|
() => stateManager.getStatus() === "RUNNING",
|
|
@@ -1225,6 +1303,19 @@ async function createSession({
|
|
|
1225
1303
|
});
|
|
1226
1304
|
throw workflow.ApplicationFailure.fromError(error);
|
|
1227
1305
|
} finally {
|
|
1306
|
+
try {
|
|
1307
|
+
await saveThreadState(
|
|
1308
|
+
threadId,
|
|
1309
|
+
stateManager.getPersistedSlice(),
|
|
1310
|
+
threadKey
|
|
1311
|
+
);
|
|
1312
|
+
} catch (persistError) {
|
|
1313
|
+
workflow.log.warn("failed to persist thread state", {
|
|
1314
|
+
agentName,
|
|
1315
|
+
threadId,
|
|
1316
|
+
error: persistError instanceof Error ? persistError.message : String(persistError)
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1228
1319
|
await callSessionEnd(exitReason, stateManager.getTurns());
|
|
1229
1320
|
if (sandboxOwned && sandboxId && sandboxOps) {
|
|
1230
1321
|
switch (sandboxShutdown) {
|
|
@@ -1261,6 +1352,13 @@ async function createSession({
|
|
|
1261
1352
|
...baseSnapshot && { hasBaseSnapshot: true },
|
|
1262
1353
|
...exitSnapshot && { hasExitSnapshot: true }
|
|
1263
1354
|
});
|
|
1355
|
+
if (onSessionExit) {
|
|
1356
|
+
onSessionExit({
|
|
1357
|
+
...sandboxId && { sandboxId },
|
|
1358
|
+
...exitSnapshot && { snapshot: exitSnapshot },
|
|
1359
|
+
threadId
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1264
1362
|
return {
|
|
1265
1363
|
threadId,
|
|
1266
1364
|
finalMessage,
|
|
@@ -1289,6 +1387,18 @@ function defineWorkflow(config, fn) {
|
|
|
1289
1387
|
return workflow;
|
|
1290
1388
|
}
|
|
1291
1389
|
|
|
1390
|
+
// src/lib/thread/keys.ts
|
|
1391
|
+
var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
|
|
1392
|
+
function getThreadListKey(threadKey, threadId) {
|
|
1393
|
+
return `${threadKey}:thread:${threadId}`;
|
|
1394
|
+
}
|
|
1395
|
+
function getThreadMetaKey(threadKey, threadId) {
|
|
1396
|
+
return `${threadKey}:meta:thread:${threadId}`;
|
|
1397
|
+
}
|
|
1398
|
+
function getThreadStateKey(threadKey, threadId) {
|
|
1399
|
+
return `${threadKey}:state:thread:${threadId}`;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1292
1402
|
// src/lib/types.ts
|
|
1293
1403
|
function isTerminalStatus(status) {
|
|
1294
1404
|
return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
|
|
@@ -1308,11 +1418,19 @@ function createAgentStateManager({
|
|
|
1308
1418
|
let systemPrompt = initialState?.systemPrompt;
|
|
1309
1419
|
const tasks = new Map(initialState?.tasks);
|
|
1310
1420
|
const {
|
|
1311
|
-
status:
|
|
1312
|
-
version:
|
|
1313
|
-
turns:
|
|
1314
|
-
tasks:
|
|
1315
|
-
tools:
|
|
1421
|
+
status: _status,
|
|
1422
|
+
version: _version,
|
|
1423
|
+
turns: _turns,
|
|
1424
|
+
tasks: _tasks,
|
|
1425
|
+
tools: _tools,
|
|
1426
|
+
systemPrompt: _systemPrompt,
|
|
1427
|
+
fileTree: _fileTree,
|
|
1428
|
+
inlineFiles: _inlineFiles,
|
|
1429
|
+
virtualFsCtx: _virtualFsCtx,
|
|
1430
|
+
totalInputTokens: _totalInputTokens,
|
|
1431
|
+
totalOutputTokens: _totalOutputTokens,
|
|
1432
|
+
cachedWriteTokens: _cachedWriteTokens,
|
|
1433
|
+
cachedReadTokens: _cachedReadTokens,
|
|
1316
1434
|
...custom
|
|
1317
1435
|
} = initialState ?? {};
|
|
1318
1436
|
const customState = custom;
|
|
@@ -1392,7 +1510,14 @@ function createAgentStateManager({
|
|
|
1392
1510
|
version++;
|
|
1393
1511
|
},
|
|
1394
1512
|
mergeUpdate(update) {
|
|
1395
|
-
|
|
1513
|
+
const { tasks: nextTasks, ...rest } = update;
|
|
1514
|
+
if (nextTasks) {
|
|
1515
|
+
tasks.clear();
|
|
1516
|
+
for (const [id, task] of nextTasks) {
|
|
1517
|
+
tasks.set(id, task);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
Object.assign(customState, rest);
|
|
1396
1521
|
version++;
|
|
1397
1522
|
},
|
|
1398
1523
|
getCurrentState() {
|
|
@@ -1430,6 +1555,12 @@ function createAgentStateManager({
|
|
|
1430
1555
|
}
|
|
1431
1556
|
return deleted;
|
|
1432
1557
|
},
|
|
1558
|
+
getPersistedSlice() {
|
|
1559
|
+
return {
|
|
1560
|
+
tasks: Array.from(tasks.entries()),
|
|
1561
|
+
custom: { ...customState }
|
|
1562
|
+
};
|
|
1563
|
+
},
|
|
1433
1564
|
updateUsage(usage) {
|
|
1434
1565
|
totalInputTokens += usage.inputTokens ?? 0;
|
|
1435
1566
|
totalOutputTokens += usage.outputTokens ?? 0;
|
|
@@ -1551,22 +1682,42 @@ function defineSubagentWorkflow(config, fn) {
|
|
|
1551
1682
|
});
|
|
1552
1683
|
}
|
|
1553
1684
|
const parentHandle = workflow.getExternalWorkflowHandle(parent.workflowId);
|
|
1685
|
+
let capturedSandboxId;
|
|
1686
|
+
let capturedSnapshot;
|
|
1687
|
+
let capturedBaseSnapshot;
|
|
1688
|
+
let capturedThreadId;
|
|
1554
1689
|
const sessionInput = {
|
|
1555
1690
|
agentName: config.name,
|
|
1556
1691
|
sandboxShutdown: effectiveShutdown,
|
|
1557
1692
|
...workflowInput.thread && { thread: workflowInput.thread },
|
|
1558
1693
|
...workflowInput.sandbox && { sandbox: workflowInput.sandbox },
|
|
1559
|
-
onSandboxReady: (sandboxId) => {
|
|
1694
|
+
onSandboxReady: ({ sandboxId, baseSnapshot }) => {
|
|
1695
|
+
capturedBaseSnapshot = baseSnapshot;
|
|
1560
1696
|
const isReuse = workflowInput.sandbox?.mode === "continue";
|
|
1561
1697
|
if (!isReuse) {
|
|
1562
1698
|
void parentHandle.signal(childSandboxReadySignal, {
|
|
1563
1699
|
childWorkflowId: workflow.workflowInfo().workflowId,
|
|
1564
|
-
sandboxId
|
|
1700
|
+
sandboxId,
|
|
1701
|
+
...baseSnapshot && { baseSnapshot }
|
|
1565
1702
|
});
|
|
1566
1703
|
}
|
|
1704
|
+
},
|
|
1705
|
+
onSessionExit: ({ sandboxId, snapshot, threadId }) => {
|
|
1706
|
+
capturedSandboxId = sandboxId;
|
|
1707
|
+
capturedSnapshot = snapshot;
|
|
1708
|
+
capturedThreadId = threadId;
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1711
|
+
const result = await fn(prompt, sessionInput, context ?? {});
|
|
1712
|
+
return {
|
|
1713
|
+
...result,
|
|
1714
|
+
...capturedThreadId !== void 0 && { threadId: capturedThreadId },
|
|
1715
|
+
...capturedSandboxId !== void 0 && { sandboxId: capturedSandboxId },
|
|
1716
|
+
...capturedSnapshot !== void 0 && { snapshot: capturedSnapshot },
|
|
1717
|
+
...capturedBaseSnapshot !== void 0 && {
|
|
1718
|
+
baseSnapshot: capturedBaseSnapshot
|
|
1567
1719
|
}
|
|
1568
1720
|
};
|
|
1569
|
-
return fn(prompt, sessionInput, context ?? {});
|
|
1570
1721
|
};
|
|
1571
1722
|
Object.defineProperty(workflow$1, "name", { value: config.name });
|
|
1572
1723
|
return Object.assign(workflow$1, {
|
|
@@ -2336,7 +2487,6 @@ var FileSystemSkillProvider = class {
|
|
|
2336
2487
|
};
|
|
2337
2488
|
|
|
2338
2489
|
// src/lib/thread/manager.ts
|
|
2339
|
-
var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
|
|
2340
2490
|
var APPEND_IDEMPOTENT_SCRIPT = `
|
|
2341
2491
|
if redis.call('EXISTS', KEYS[1]) == 1 then
|
|
2342
2492
|
return 0
|
|
@@ -2348,8 +2498,8 @@ redis.call('EXPIRE', KEYS[2], tonumber(ARGV[1]))
|
|
|
2348
2498
|
redis.call('SET', KEYS[1], '1', 'EX', tonumber(ARGV[1]))
|
|
2349
2499
|
return 1
|
|
2350
2500
|
`;
|
|
2351
|
-
function
|
|
2352
|
-
return
|
|
2501
|
+
function getDedupKey(threadId, id) {
|
|
2502
|
+
return `dedup:${id}:thread:${threadId}`;
|
|
2353
2503
|
}
|
|
2354
2504
|
function createThreadManager(config) {
|
|
2355
2505
|
const {
|
|
@@ -2360,8 +2510,9 @@ function createThreadManager(config) {
|
|
|
2360
2510
|
deserialize = (raw) => JSON.parse(raw),
|
|
2361
2511
|
idOf
|
|
2362
2512
|
} = config;
|
|
2363
|
-
const redisKey =
|
|
2364
|
-
const metaKey =
|
|
2513
|
+
const redisKey = getThreadListKey(key, threadId);
|
|
2514
|
+
const metaKey = getThreadMetaKey(key, threadId);
|
|
2515
|
+
const stateKey = getThreadStateKey(key, threadId);
|
|
2365
2516
|
async function assertThreadExists() {
|
|
2366
2517
|
const exists = await redis.exists(metaKey);
|
|
2367
2518
|
if (!exists) {
|
|
@@ -2383,7 +2534,7 @@ function createThreadManager(config) {
|
|
|
2383
2534
|
await assertThreadExists();
|
|
2384
2535
|
if (idOf) {
|
|
2385
2536
|
const dedupId = messages.map(idOf).join(":");
|
|
2386
|
-
const dedupKey =
|
|
2537
|
+
const dedupKey = getDedupKey(threadId, dedupId);
|
|
2387
2538
|
await redis.eval(
|
|
2388
2539
|
APPEND_IDEMPOTENT_SCRIPT,
|
|
2389
2540
|
2,
|
|
@@ -2400,34 +2551,98 @@ function createThreadManager(config) {
|
|
|
2400
2551
|
async fork(newThreadId) {
|
|
2401
2552
|
await assertThreadExists();
|
|
2402
2553
|
const data = await redis.lrange(redisKey, 0, -1);
|
|
2554
|
+
const stateRaw = await redis.get(stateKey);
|
|
2403
2555
|
const forked = createThreadManager({
|
|
2404
2556
|
...config,
|
|
2405
2557
|
threadId: newThreadId
|
|
2406
2558
|
});
|
|
2407
2559
|
await forked.initialize();
|
|
2408
2560
|
if (data.length > 0) {
|
|
2409
|
-
const newKey =
|
|
2561
|
+
const newKey = getThreadListKey(key, newThreadId);
|
|
2410
2562
|
await redis.rpush(newKey, ...data);
|
|
2411
2563
|
await redis.expire(newKey, THREAD_TTL_SECONDS);
|
|
2412
2564
|
}
|
|
2565
|
+
if (stateRaw != null) {
|
|
2566
|
+
const newStateKey = getThreadStateKey(key, newThreadId);
|
|
2567
|
+
await redis.set(newStateKey, stateRaw, "EX", THREAD_TTL_SECONDS);
|
|
2568
|
+
}
|
|
2413
2569
|
return forked;
|
|
2414
2570
|
},
|
|
2571
|
+
async replaceAll(messages) {
|
|
2572
|
+
await assertThreadExists();
|
|
2573
|
+
if (!idOf) {
|
|
2574
|
+
throw new Error(
|
|
2575
|
+
"replaceAll requires the thread manager to be configured with `idOf`"
|
|
2576
|
+
);
|
|
2577
|
+
}
|
|
2578
|
+
const existing = await redis.lrange(redisKey, 0, -1);
|
|
2579
|
+
const existingIds = existing.map((raw) => idOf(deserialize(raw))).filter((id) => typeof id === "string");
|
|
2580
|
+
await redis.del(redisKey);
|
|
2581
|
+
if (existingIds.length > 0) {
|
|
2582
|
+
await redis.del(
|
|
2583
|
+
...existingIds.map((id) => getDedupKey(threadId, id))
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
if (messages.length > 0) {
|
|
2587
|
+
await redis.rpush(redisKey, ...messages.map(serialize));
|
|
2588
|
+
await redis.expire(redisKey, THREAD_TTL_SECONDS);
|
|
2589
|
+
}
|
|
2590
|
+
await redis.expire(metaKey, THREAD_TTL_SECONDS);
|
|
2591
|
+
},
|
|
2415
2592
|
async delete() {
|
|
2416
|
-
await redis.del(redisKey, metaKey);
|
|
2593
|
+
await redis.del(redisKey, metaKey, stateKey);
|
|
2594
|
+
},
|
|
2595
|
+
async loadState() {
|
|
2596
|
+
const raw = await redis.get(stateKey);
|
|
2597
|
+
if (raw == null) return null;
|
|
2598
|
+
return JSON.parse(raw);
|
|
2599
|
+
},
|
|
2600
|
+
async saveState(state) {
|
|
2601
|
+
await assertThreadExists();
|
|
2602
|
+
await redis.set(
|
|
2603
|
+
stateKey,
|
|
2604
|
+
JSON.stringify(state),
|
|
2605
|
+
"EX",
|
|
2606
|
+
THREAD_TTL_SECONDS
|
|
2607
|
+
);
|
|
2608
|
+
},
|
|
2609
|
+
async deleteState() {
|
|
2610
|
+
await redis.del(stateKey);
|
|
2417
2611
|
},
|
|
2418
2612
|
async length() {
|
|
2419
2613
|
await assertThreadExists();
|
|
2420
2614
|
return redis.llen(redisKey);
|
|
2421
2615
|
},
|
|
2422
|
-
async
|
|
2616
|
+
async truncateFromId(messageId) {
|
|
2423
2617
|
await assertThreadExists();
|
|
2424
|
-
if (
|
|
2618
|
+
if (!idOf) {
|
|
2619
|
+
throw new Error(
|
|
2620
|
+
"truncateFromId requires the thread manager to be configured with `idOf`"
|
|
2621
|
+
);
|
|
2622
|
+
}
|
|
2623
|
+
const data = await redis.lrange(redisKey, 0, -1);
|
|
2624
|
+
let idx = -1;
|
|
2625
|
+
const removedIds = [];
|
|
2626
|
+
for (let i = 0; i < data.length; i++) {
|
|
2627
|
+
const raw = data[i];
|
|
2628
|
+
if (raw === void 0) continue;
|
|
2629
|
+
const id = idOf(deserialize(raw));
|
|
2630
|
+
if (idx === -1 && id === messageId) idx = i;
|
|
2631
|
+
if (idx !== -1) removedIds.push(id);
|
|
2632
|
+
}
|
|
2633
|
+
if (idx === -1) return;
|
|
2634
|
+
if (idx === 0) {
|
|
2425
2635
|
await redis.del(redisKey);
|
|
2426
2636
|
await redis.expire(metaKey, THREAD_TTL_SECONDS);
|
|
2427
2637
|
} else {
|
|
2428
|
-
await redis.ltrim(redisKey, 0,
|
|
2638
|
+
await redis.ltrim(redisKey, 0, idx - 1);
|
|
2429
2639
|
await redis.expire(redisKey, THREAD_TTL_SECONDS);
|
|
2430
2640
|
}
|
|
2641
|
+
if (removedIds.length > 0) {
|
|
2642
|
+
await redis.del(
|
|
2643
|
+
...removedIds.map((id) => getDedupKey(threadId, id))
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2431
2646
|
}
|
|
2432
2647
|
};
|
|
2433
2648
|
}
|
|
@@ -3242,11 +3457,13 @@ var toTree = async (fs, opts = {}) => {
|
|
|
3242
3457
|
return base + subtree;
|
|
3243
3458
|
};
|
|
3244
3459
|
|
|
3460
|
+
exports.DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT = DEFAULT_SUBAGENT_WORKFLOW_RUN_TIMEOUT;
|
|
3245
3461
|
exports.FileSystemSkillProvider = FileSystemSkillProvider;
|
|
3246
3462
|
exports.NodeFsSandboxFileSystem = NodeFsSandboxFileSystem;
|
|
3247
3463
|
exports.SandboxManager = SandboxManager;
|
|
3248
3464
|
exports.SandboxNotFoundError = SandboxNotFoundError;
|
|
3249
3465
|
exports.SandboxNotSupportedError = SandboxNotSupportedError;
|
|
3466
|
+
exports.THREAD_TTL_SECONDS = THREAD_TTL_SECONDS;
|
|
3250
3467
|
exports.VirtualFileSystem = VirtualFileSystem;
|
|
3251
3468
|
exports.applyVirtualTreeMutations = applyVirtualTreeMutations;
|
|
3252
3469
|
exports.askUserQuestionTool = askUserQuestionTool;
|
|
@@ -3278,6 +3495,8 @@ exports.filesWithMimeType = filesWithMimeType;
|
|
|
3278
3495
|
exports.formatVirtualFileTree = formatVirtualFileTree;
|
|
3279
3496
|
exports.getActivityContext = getActivityContext;
|
|
3280
3497
|
exports.getShortId = getShortId;
|
|
3498
|
+
exports.getThreadListKey = getThreadListKey;
|
|
3499
|
+
exports.getThreadMetaKey = getThreadMetaKey;
|
|
3281
3500
|
exports.globHandler = globHandler;
|
|
3282
3501
|
exports.globTool = globTool;
|
|
3283
3502
|
exports.grepTool = grepTool;
|