zeitlich 0.2.47 → 0.2.48
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 +2 -0
- package/dist/{activities-CPwKoUlD.d.cts → activities-BlQR5gX4.d.cts} +3 -3
- package/dist/{activities-DlaBxNID.d.ts → activities-DCaIPQBT.d.ts} +3 -3
- 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/workflow.d.cts +5 -5
- package/dist/adapters/thread/anthropic/workflow.d.ts +5 -5
- 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/workflow.d.cts +6 -6
- package/dist/adapters/thread/google-genai/workflow.d.ts +6 -6
- 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/workflow.d.cts +5 -5
- package/dist/adapters/thread/langchain/workflow.d.ts +5 -5
- package/dist/{cold-store-Z2wvK2cV.d.cts → cold-store-UL13Sstw.d.cts} +1 -1
- package/dist/{cold-store-BDgJpwLI.d.ts → cold-store-aD4TSKlU.d.ts} +1 -1
- package/dist/index.cjs +48 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +48 -10
- package/dist/index.js.map +1 -1
- package/dist/{proxy-CDh3Rsa7.d.cts → proxy-BAty3CWM.d.cts} +1 -1
- package/dist/{proxy-Du8ggERu.d.ts → proxy-mbnwBhHw.d.ts} +1 -1
- package/dist/{thread-manager-Dw96FKH1.d.ts → thread-manager-CICj68PI.d.ts} +2 -2
- package/dist/{thread-manager-BjoYYXgd.d.cts → thread-manager-DsXvJ5cJ.d.cts} +2 -2
- package/dist/{thread-manager-DtHYws2F.d.ts → thread-manager-DtEtbUkp.d.ts} +2 -2
- package/dist/{thread-manager-D8zKNFZ9.d.cts → thread-manager-R6c3lnJy.d.cts} +2 -2
- package/dist/{types-BMJrsHo0.d.cts → types-DDLPnxBh.d.cts} +1 -1
- package/dist/{types-CtdOquo3.d.ts → types-DF4wzWQG.d.ts} +1 -1
- package/dist/{types-DNEl5uxQ.d.cts → types-DWeyCTYK.d.cts} +31 -0
- package/dist/{types-qQVZfhoT.d.ts → types-DwBYd0ij.d.ts} +31 -0
- package/dist/{workflow-BH9ImDGq.d.cts → workflow-DVNPR7eX.d.cts} +1 -1
- package/dist/{workflow-Cdw3-RNB.d.ts → workflow-DdaU7_j4.d.ts} +1 -1
- package/dist/workflow.cjs +48 -10
- 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 +48 -10
- package/dist/workflow.js.map +1 -1
- package/package.json +6 -6
- package/src/lib/lifecycle.ts +13 -1
- package/src/lib/session/session-edge-cases.integration.test.ts +44 -0
- package/src/lib/session/session.ts +15 -0
- package/src/lib/subagent/handler.ts +32 -6
- package/src/lib/subagent/subagent.integration.test.ts +41 -2
- package/src/lib/tool-router/router-edge-cases.integration.test.ts +36 -0
- package/src/lib/tool-router/router.ts +21 -3
- package/src/lib/tool-router/types.ts +20 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zeitlich",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.48",
|
|
4
4
|
"description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -208,11 +208,11 @@
|
|
|
208
208
|
"@e2b/code-interpreter": "^2.3.3",
|
|
209
209
|
"@eslint/js": "^10.0.1",
|
|
210
210
|
"@google/genai": "^1.44.0",
|
|
211
|
-
"@langchain/core": "^1.1.
|
|
212
|
-
"@temporalio/common": "^1.17.
|
|
213
|
-
"@temporalio/envconfig": "^1.17.
|
|
214
|
-
"@temporalio/worker": "^1.17.
|
|
215
|
-
"@temporalio/workflow": "^1.17.
|
|
211
|
+
"@langchain/core": "^1.1.48",
|
|
212
|
+
"@temporalio/common": "^1.17.2",
|
|
213
|
+
"@temporalio/envconfig": "^1.17.2",
|
|
214
|
+
"@temporalio/worker": "^1.17.2",
|
|
215
|
+
"@temporalio/workflow": "^1.17.2",
|
|
216
216
|
"@types/node": "^25.3.3",
|
|
217
217
|
"eslint": "^10.0.2",
|
|
218
218
|
"husky": "^9.1.7",
|
package/src/lib/lifecycle.ts
CHANGED
|
@@ -11,11 +11,23 @@
|
|
|
11
11
|
* continue there. When the adapter has `onForkPrepareThread` and/or
|
|
12
12
|
* `onForkTransform` hooks configured, they are applied once to the forked
|
|
13
13
|
* thread before the session starts.
|
|
14
|
+
*
|
|
15
|
+
* The optional `truncateAfterFork.fromMessageId` directs the session to
|
|
16
|
+
* call `truncateThread` on the freshly forked thread immediately after
|
|
17
|
+
* the fork, dropping that message and everything after. Used by
|
|
18
|
+
* subagents that fork their parent's thread mid-tool-call to strip the
|
|
19
|
+
* orphan assistant `tool_use` block (the one whose `tool_result` will
|
|
20
|
+
* never arrive in the child's thread) so the first model call doesn't
|
|
21
|
+
* reject on an unmatched tool-use/tool-result pair.
|
|
14
22
|
*/
|
|
15
23
|
export type ThreadInit =
|
|
16
24
|
| { mode: "new"; threadId?: string }
|
|
17
25
|
| { mode: "continue"; threadId: string }
|
|
18
|
-
| {
|
|
26
|
+
| {
|
|
27
|
+
mode: "fork";
|
|
28
|
+
threadId: string;
|
|
29
|
+
truncateAfterFork?: { fromMessageId: string };
|
|
30
|
+
};
|
|
19
31
|
|
|
20
32
|
// ============================================================================
|
|
21
33
|
// Sandbox lifecycle
|
|
@@ -660,6 +660,50 @@ describe("createSession edge cases", () => {
|
|
|
660
660
|
const forkOp = forkOps[0];
|
|
661
661
|
if (!forkOp) throw new Error("expected fork op");
|
|
662
662
|
expect(forkOp.args[0]).toBe("original-thread");
|
|
663
|
+
|
|
664
|
+
// No truncateAfterFork directive ⇒ no truncate call.
|
|
665
|
+
const truncateOps = log.filter((l) => l.op === "truncateThread");
|
|
666
|
+
expect(truncateOps).toHaveLength(0);
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it("fork thread mode truncates the forked thread at fromMessageId when truncateAfterFork is set", async () => {
|
|
670
|
+
const { ops, log } = createMockThreadOps();
|
|
671
|
+
|
|
672
|
+
const session = await createSession({
|
|
673
|
+
agentName: "TestAgent",
|
|
674
|
+
thread: {
|
|
675
|
+
mode: "fork",
|
|
676
|
+
threadId: "parent-thread",
|
|
677
|
+
truncateAfterFork: { fromMessageId: "parent-asst-msg-1" },
|
|
678
|
+
},
|
|
679
|
+
runAgent: createScriptedRunAgent([
|
|
680
|
+
{ message: "forked & continued", toolCalls: [] },
|
|
681
|
+
]),
|
|
682
|
+
threadOps: ops,
|
|
683
|
+
buildContextMessage: () => "continue",
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
const stateManager = createAgentStateManager({
|
|
687
|
+
initialState: { systemPrompt: "test" },
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
const result = await session.runSession({ stateManager });
|
|
691
|
+
|
|
692
|
+
expect(result.exitReason).toBe("completed");
|
|
693
|
+
const forkedThreadId = result.threadId;
|
|
694
|
+
expect(forkedThreadId).not.toBe("parent-thread");
|
|
695
|
+
|
|
696
|
+
// Order matters: fork must happen before truncate, otherwise the
|
|
697
|
+
// truncate would no-op against an empty thread.
|
|
698
|
+
const forkIdx = log.findIndex((l) => l.op === "forkThread");
|
|
699
|
+
const truncIdx = log.findIndex((l) => l.op === "truncateThread");
|
|
700
|
+
expect(forkIdx).toBeGreaterThanOrEqual(0);
|
|
701
|
+
expect(truncIdx).toBeGreaterThan(forkIdx);
|
|
702
|
+
|
|
703
|
+
const truncOp = log[truncIdx];
|
|
704
|
+
if (!truncOp) throw new Error("expected truncate op");
|
|
705
|
+
expect(truncOp.args[0]).toBe(forkedThreadId);
|
|
706
|
+
expect(truncOp.args[1]).toBe("parent-asst-msg-1");
|
|
663
707
|
});
|
|
664
708
|
|
|
665
709
|
// --- maxTurns of 1 ---
|
|
@@ -196,6 +196,7 @@ export async function createSession<
|
|
|
196
196
|
appendSystemMessage,
|
|
197
197
|
appendAgentMessage,
|
|
198
198
|
forkThread,
|
|
199
|
+
truncateThread,
|
|
199
200
|
loadThreadState,
|
|
200
201
|
saveThreadState,
|
|
201
202
|
hydrateThread,
|
|
@@ -399,6 +400,17 @@ export async function createSession<
|
|
|
399
400
|
// is already hot or when no cold tier is wired.
|
|
400
401
|
await hydrateThread(sourceThreadId, threadKey);
|
|
401
402
|
await forkThread(sourceThreadId, threadId, threadKey);
|
|
403
|
+
// If the caller asked to drop the tail of the forked thread
|
|
404
|
+
// (e.g. subagent forking its parent mid-tool-call needs to
|
|
405
|
+
// strip the orphan assistant `tool_use`), do it now — before
|
|
406
|
+
// any rehydration / state load so the truncated thread is
|
|
407
|
+
// what subsequent reads see.
|
|
408
|
+
const truncate = (
|
|
409
|
+
threadInit as { mode: "fork"; truncateAfterFork?: { fromMessageId: string } }
|
|
410
|
+
).truncateAfterFork;
|
|
411
|
+
if (truncate?.fromMessageId) {
|
|
412
|
+
await truncateThread(threadId, truncate.fromMessageId, threadKey);
|
|
413
|
+
}
|
|
402
414
|
const forkedSlice = await loadThreadState(threadId, threadKey);
|
|
403
415
|
if (forkedSlice) rehydrateFromSlice(forkedSlice);
|
|
404
416
|
} else if (threadMode === "continue") {
|
|
@@ -570,6 +582,9 @@ export async function createSession<
|
|
|
570
582
|
{
|
|
571
583
|
turn: currentTurn,
|
|
572
584
|
...(sandboxId !== undefined && { sandboxId }),
|
|
585
|
+
...(assistantId !== undefined && {
|
|
586
|
+
assistantMessageId: assistantId,
|
|
587
|
+
}),
|
|
573
588
|
}
|
|
574
589
|
);
|
|
575
590
|
|
|
@@ -244,18 +244,44 @@ export function createSubagentHandler<
|
|
|
244
244
|
// `thread: "fork" | "continue"` — `thread: "new"` always starts
|
|
245
245
|
// fresh regardless of the source.
|
|
246
246
|
const newThreadSource = config.newThreadSource ?? "new";
|
|
247
|
+
const usingParentFallback =
|
|
248
|
+
allowsContinuation &&
|
|
249
|
+
!args.threadId &&
|
|
250
|
+
newThreadSource === "from-parent";
|
|
247
251
|
const continuationThreadId = !allowsContinuation
|
|
248
252
|
? undefined
|
|
249
|
-
: (args.threadId ??
|
|
250
|
-
(newThreadSource === "from-parent" ? context.threadId : undefined));
|
|
253
|
+
: (args.threadId ?? (usingParentFallback ? context.threadId : undefined));
|
|
251
254
|
|
|
252
255
|
// --- Build thread init ---
|
|
253
256
|
let thread: ThreadInit | undefined;
|
|
254
257
|
if (continuationThreadId) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
258
|
+
if (threadMode === "fork") {
|
|
259
|
+
// When falling back to the parent's thread for a fork, the
|
|
260
|
+
// parent is mid-tool-call: its assistant message containing the
|
|
261
|
+
// `Subagent` tool_use has already been persisted, but its
|
|
262
|
+
// matching tool_result hasn't. Forking verbatim would leave an
|
|
263
|
+
// orphan tool_use at the tail of the child thread, which most
|
|
264
|
+
// model APIs reject on the very next call. Have the child's
|
|
265
|
+
// session truncate that assistant message (and anything after)
|
|
266
|
+
// immediately after the fork so the first model call sees a
|
|
267
|
+
// well-formed history.
|
|
268
|
+
thread = {
|
|
269
|
+
mode: "fork",
|
|
270
|
+
threadId: continuationThreadId,
|
|
271
|
+
...(usingParentFallback && context.assistantMessageId
|
|
272
|
+
? {
|
|
273
|
+
truncateAfterFork: {
|
|
274
|
+
fromMessageId: context.assistantMessageId,
|
|
275
|
+
},
|
|
276
|
+
}
|
|
277
|
+
: {}),
|
|
278
|
+
};
|
|
279
|
+
} else {
|
|
280
|
+
thread = {
|
|
281
|
+
mode: "continue",
|
|
282
|
+
threadId: continuationThreadId,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
259
285
|
}
|
|
260
286
|
|
|
261
287
|
// --- Build sandbox init ---
|
|
@@ -653,6 +653,40 @@ describe("createSubagentHandler", () => {
|
|
|
653
653
|
|
|
654
654
|
await handler(
|
|
655
655
|
{ subagent: "parent-fork", description: "test", prompt: "test" },
|
|
656
|
+
{
|
|
657
|
+
threadId: "parent-t",
|
|
658
|
+
toolCallId: "tc",
|
|
659
|
+
toolName: "Subagent",
|
|
660
|
+
assistantMessageId: "parent-asst-msg-1",
|
|
661
|
+
}
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
const lastCall = execMock.mock.calls[execMock.mock.calls.length - 1];
|
|
665
|
+
if (!lastCall) throw new Error("expected executeChild call");
|
|
666
|
+
const workflowInput = lastCall[1].args[1] as SubagentWorkflowInput;
|
|
667
|
+
expect(workflowInput.thread).toEqual({
|
|
668
|
+
mode: "fork",
|
|
669
|
+
threadId: "parent-t",
|
|
670
|
+
truncateAfterFork: { fromMessageId: "parent-asst-msg-1" },
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it("omits truncateAfterFork on parent-fallback fork when assistantMessageId is absent from context", async () => {
|
|
675
|
+
const { executeChild } = await import("@temporalio/workflow");
|
|
676
|
+
const execMock = executeChild as ReturnType<typeof vi.fn>;
|
|
677
|
+
|
|
678
|
+
const subagent: SubagentConfig = {
|
|
679
|
+
agentName: "parent-fork-no-asst",
|
|
680
|
+
description: "Forks parent thread by default",
|
|
681
|
+
workflow: mockWorkflow(),
|
|
682
|
+
thread: "fork",
|
|
683
|
+
newThreadSource: "from-parent",
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
const { handler } = createSubagentHandler([subagent]);
|
|
687
|
+
|
|
688
|
+
await handler(
|
|
689
|
+
{ subagent: "parent-fork-no-asst", description: "test", prompt: "test" },
|
|
656
690
|
{ threadId: "parent-t", toolCallId: "tc", toolName: "Subagent" }
|
|
657
691
|
);
|
|
658
692
|
|
|
@@ -693,7 +727,7 @@ describe("createSubagentHandler", () => {
|
|
|
693
727
|
});
|
|
694
728
|
});
|
|
695
729
|
|
|
696
|
-
it("prefers args.threadId over the parent source when both are available", async () => {
|
|
730
|
+
it("prefers args.threadId over the parent source when both are available and skips truncateAfterFork", async () => {
|
|
697
731
|
const { executeChild } = await import("@temporalio/workflow");
|
|
698
732
|
const execMock = executeChild as ReturnType<typeof vi.fn>;
|
|
699
733
|
|
|
@@ -714,7 +748,12 @@ describe("createSubagentHandler", () => {
|
|
|
714
748
|
prompt: "test",
|
|
715
749
|
threadId: "explicit-prev",
|
|
716
750
|
},
|
|
717
|
-
{
|
|
751
|
+
{
|
|
752
|
+
threadId: "parent-t",
|
|
753
|
+
toolCallId: "tc",
|
|
754
|
+
toolName: "Subagent",
|
|
755
|
+
assistantMessageId: "parent-asst-msg-1",
|
|
756
|
+
}
|
|
718
757
|
);
|
|
719
758
|
|
|
720
759
|
const lastCall = execMock.mock.calls[execMock.mock.calls.length - 1];
|
|
@@ -134,6 +134,42 @@ describe("createToolRouter edge cases", () => {
|
|
|
134
134
|
expect(appendSpy.calls).toHaveLength(0);
|
|
135
135
|
});
|
|
136
136
|
|
|
137
|
+
// --- assistantMessageId propagation into RouterContext ---
|
|
138
|
+
|
|
139
|
+
it("forwards assistantMessageId from ProcessToolCallsContext into RouterContext", async () => {
|
|
140
|
+
let capturedAssistantMessageId: string | undefined;
|
|
141
|
+
const captureTool = defineTool({
|
|
142
|
+
name: "Capture" as const,
|
|
143
|
+
description: "captures router context",
|
|
144
|
+
schema: z.object({}),
|
|
145
|
+
handler: async (
|
|
146
|
+
_args: Record<string, never>,
|
|
147
|
+
ctx: { assistantMessageId?: string }
|
|
148
|
+
): Promise<ToolHandlerResponse<null>> => {
|
|
149
|
+
capturedAssistantMessageId = ctx.assistantMessageId;
|
|
150
|
+
return { toolResponse: "ok", data: null };
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const router = createToolRouter({
|
|
155
|
+
tools: { Capture: captureTool } as const,
|
|
156
|
+
threadId: "t-1",
|
|
157
|
+
appendToolResult: appendSpy.fn,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const parsed = router.parseToolCall({
|
|
161
|
+
id: "tc-1",
|
|
162
|
+
name: "Capture",
|
|
163
|
+
args: {},
|
|
164
|
+
});
|
|
165
|
+
await router.processToolCalls([parsed], {
|
|
166
|
+
turn: 1,
|
|
167
|
+
assistantMessageId: "asst-msg-42",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(capturedAssistantMessageId).toBe("asst-msg-42");
|
|
171
|
+
});
|
|
172
|
+
|
|
137
173
|
// --- Both global and per-tool pre-hooks run in order ---
|
|
138
174
|
|
|
139
175
|
it("global pre-hook runs before per-tool pre-hook", async () => {
|
|
@@ -220,7 +220,8 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
220
220
|
toolCall: ParsedToolCallUnion<T>,
|
|
221
221
|
turn: number,
|
|
222
222
|
sandboxId?: string,
|
|
223
|
-
onRewindRequested?: (signal: RewindSignal) => void
|
|
223
|
+
onRewindRequested?: (signal: RewindSignal) => void,
|
|
224
|
+
assistantMessageId?: string
|
|
224
225
|
): Promise<ProcessedToolCall> {
|
|
225
226
|
const startTime = Date.now();
|
|
226
227
|
const tool = toolMap.get(toolCall.name);
|
|
@@ -263,6 +264,7 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
263
264
|
toolCallId: toolCall.id,
|
|
264
265
|
toolName: toolCall.name,
|
|
265
266
|
...(sandboxId !== undefined && { sandboxId }),
|
|
267
|
+
...(assistantMessageId !== undefined && { assistantMessageId }),
|
|
266
268
|
};
|
|
267
269
|
const response = await tool.handler(
|
|
268
270
|
effectiveArgs as Parameters<typeof tool.handler>[0],
|
|
@@ -419,6 +421,7 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
419
421
|
|
|
420
422
|
const turn = context?.turn ?? 0;
|
|
421
423
|
const sandboxId = context?.sandboxId;
|
|
424
|
+
const assistantMessageId = context?.assistantMessageId;
|
|
422
425
|
|
|
423
426
|
let rewindSignal: RewindSignal | undefined;
|
|
424
427
|
|
|
@@ -435,7 +438,13 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
435
438
|
const outcomes = await scope.run(async () =>
|
|
436
439
|
Promise.allSettled(
|
|
437
440
|
toolCalls.map((tc) =>
|
|
438
|
-
processToolCall(
|
|
441
|
+
processToolCall(
|
|
442
|
+
tc,
|
|
443
|
+
turn,
|
|
444
|
+
sandboxId,
|
|
445
|
+
onRewindRequested,
|
|
446
|
+
assistantMessageId
|
|
447
|
+
)
|
|
439
448
|
)
|
|
440
449
|
)
|
|
441
450
|
);
|
|
@@ -457,7 +466,13 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
457
466
|
|
|
458
467
|
const results: ToolCallResultUnion<TResults>[] = [];
|
|
459
468
|
for (const toolCall of toolCalls) {
|
|
460
|
-
const outcome = await processToolCall(
|
|
469
|
+
const outcome = await processToolCall(
|
|
470
|
+
toolCall,
|
|
471
|
+
turn,
|
|
472
|
+
sandboxId,
|
|
473
|
+
undefined,
|
|
474
|
+
assistantMessageId
|
|
475
|
+
);
|
|
461
476
|
if (outcome.kind === "rewind") {
|
|
462
477
|
rewindSignal = outcome.signal;
|
|
463
478
|
break;
|
|
@@ -492,6 +507,9 @@ export function createToolRouter<T extends ToolMap>(
|
|
|
492
507
|
...(context?.sandboxId !== undefined && {
|
|
493
508
|
sandboxId: context.sandboxId,
|
|
494
509
|
}),
|
|
510
|
+
...(context?.assistantMessageId !== undefined && {
|
|
511
|
+
assistantMessageId: context.assistantMessageId,
|
|
512
|
+
}),
|
|
495
513
|
};
|
|
496
514
|
const response = await handler(
|
|
497
515
|
toolCall.args as ToolArgs<T, TName>,
|
|
@@ -178,6 +178,18 @@ export interface RouterContext {
|
|
|
178
178
|
toolCallId: string;
|
|
179
179
|
toolName: string;
|
|
180
180
|
sandboxId?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Id of the assistant message that issued this tool call (the message
|
|
183
|
+
* the session passed as `assistantMessageId` into `runAgent`). Present
|
|
184
|
+
* for any tool call processed through `processToolCalls` from a
|
|
185
|
+
* session; may be absent when the router is driven manually (e.g.
|
|
186
|
+
* tests, custom orchestrators).
|
|
187
|
+
*
|
|
188
|
+
* Subagent handlers that fork the parent's thread mid-call use this
|
|
189
|
+
* to truncate the orphan trailing assistant message from the forked
|
|
190
|
+
* thread so the child's first model call sees a well-formed history.
|
|
191
|
+
*/
|
|
192
|
+
assistantMessageId?: string;
|
|
181
193
|
}
|
|
182
194
|
|
|
183
195
|
/**
|
|
@@ -294,6 +306,14 @@ export interface ProcessToolCallsContext {
|
|
|
294
306
|
turn?: number;
|
|
295
307
|
/** Active sandbox ID (when a sandbox is configured for this session) */
|
|
296
308
|
sandboxId?: string;
|
|
309
|
+
/**
|
|
310
|
+
* Id of the assistant message that produced these tool calls. The
|
|
311
|
+
* router forwards it into every handler's {@link RouterContext} so
|
|
312
|
+
* handlers can reference the message they were issued from (e.g.
|
|
313
|
+
* subagent forks that need to truncate the orphan assistant message
|
|
314
|
+
* out of a parent-forked thread).
|
|
315
|
+
*/
|
|
316
|
+
assistantMessageId?: string;
|
|
297
317
|
}
|
|
298
318
|
|
|
299
319
|
/**
|