zeitlich 0.2.19 → 0.2.21

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.
Files changed (64) hide show
  1. package/dist/adapters/sandbox/daytona/index.cjs +25 -10
  2. package/dist/adapters/sandbox/daytona/index.cjs.map +1 -1
  3. package/dist/adapters/sandbox/daytona/index.d.cts +4 -1
  4. package/dist/adapters/sandbox/daytona/index.d.ts +4 -1
  5. package/dist/adapters/sandbox/daytona/index.js +25 -10
  6. package/dist/adapters/sandbox/daytona/index.js.map +1 -1
  7. package/dist/adapters/sandbox/virtual/index.d.cts +4 -3
  8. package/dist/adapters/sandbox/virtual/index.d.ts +4 -3
  9. package/dist/adapters/thread/google-genai/index.cjs +69 -24
  10. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  11. package/dist/adapters/thread/google-genai/index.d.cts +10 -10
  12. package/dist/adapters/thread/google-genai/index.d.ts +10 -10
  13. package/dist/adapters/thread/google-genai/index.js +69 -24
  14. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  15. package/dist/adapters/thread/langchain/index.cjs +76 -69
  16. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  17. package/dist/adapters/thread/langchain/index.d.cts +11 -11
  18. package/dist/adapters/thread/langchain/index.d.ts +11 -11
  19. package/dist/adapters/thread/langchain/index.js +76 -69
  20. package/dist/adapters/thread/langchain/index.js.map +1 -1
  21. package/dist/index.cjs +198 -118
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +14 -14
  24. package/dist/index.d.ts +14 -14
  25. package/dist/index.js +198 -118
  26. package/dist/index.js.map +1 -1
  27. package/dist/{queries-D8T4pEeu.d.ts → queries-6Avfh74U.d.ts} +1 -1
  28. package/dist/{queries-D22uWTOb.d.cts → queries-CHa2iv_I.d.cts} +1 -1
  29. package/dist/{types-CxWLeJTB.d.ts → types-BkAYmc96.d.ts} +6 -6
  30. package/dist/{types-CCfJb5Jl.d.cts → types-CES_30qx.d.cts} +6 -6
  31. package/dist/{types-DjT78Sdp.d.cts → types-YbL7JpEA.d.cts} +4 -2
  32. package/dist/{types-DjT78Sdp.d.ts → types-YbL7JpEA.d.ts} +4 -2
  33. package/dist/workflow.cjs +68 -34
  34. package/dist/workflow.cjs.map +1 -1
  35. package/dist/workflow.d.cts +16 -12
  36. package/dist/workflow.d.ts +16 -12
  37. package/dist/workflow.js +68 -34
  38. package/dist/workflow.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/adapters/sandbox/daytona/filesystem.ts +21 -12
  41. package/src/adapters/sandbox/daytona/index.ts +24 -23
  42. package/src/adapters/thread/google-genai/activities.ts +11 -9
  43. package/src/adapters/thread/google-genai/model-invoker.ts +6 -11
  44. package/src/adapters/thread/google-genai/thread-manager.ts +44 -29
  45. package/src/adapters/thread/langchain/activities.ts +6 -4
  46. package/src/adapters/thread/langchain/thread-manager.ts +55 -27
  47. package/src/lib/session/session-edge-cases.integration.test.ts +20 -2
  48. package/src/lib/session/session.integration.test.ts +16 -2
  49. package/src/lib/session/session.ts +7 -5
  50. package/src/lib/session/types.ts +9 -3
  51. package/src/lib/subagent/handler.ts +1 -1
  52. package/src/lib/subagent/subagent.integration.test.ts +5 -4
  53. package/src/lib/subagent/tool.ts +1 -1
  54. package/src/lib/thread/index.ts +0 -1
  55. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +20 -21
  56. package/src/lib/tool-router/auto-append.ts +3 -2
  57. package/src/lib/tool-router/router-edge-cases.integration.test.ts +64 -23
  58. package/src/lib/tool-router/router.integration.test.ts +60 -23
  59. package/src/lib/tool-router/router.ts +58 -29
  60. package/src/lib/tool-router/types.ts +12 -7
  61. package/src/lib/workflow.test.ts +18 -6
  62. package/src/lib/workflow.ts +13 -3
  63. package/src/tools/task-create/handler.ts +3 -6
  64. package/src/workflow.ts +2 -2
@@ -5,6 +5,7 @@ import type { ThreadOps } from "./types";
5
5
  import type { RunAgentActivity } from "../model/types";
6
6
  import type { RawToolCall } from "../tool-router/types";
7
7
  import type { SandboxOps } from "../sandbox/types";
8
+ import type { ActivityInterfaceFor } from "@temporalio/workflow";
8
9
 
9
10
  let idCounter = 0;
10
11
 
@@ -51,9 +52,26 @@ type TurnScript = {
51
52
  usage?: TokenUsage;
52
53
  };
53
54
 
55
+ /**
56
+ * Wraps every method on a ThreadOps object so it also has `.executeWithOptions()`,
57
+ * matching Temporal's `ActivityInterfaceFor<ThreadOps>` shape.
58
+ */
59
+ function toActivityInterface(
60
+ raw: ThreadOps,
61
+ ): ActivityInterfaceFor<ThreadOps> {
62
+ const result = {} as Record<string, unknown>;
63
+ for (const [key, fn] of Object.entries(raw)) {
64
+ const wrapped = (...args: unknown[]) => (fn as (...a: unknown[]) => unknown)(...args);
65
+ wrapped.executeWithOptions = (_opts: unknown, args: unknown[]) =>
66
+ (fn as (...a: unknown[]) => unknown)(...args);
67
+ result[key] = wrapped;
68
+ }
69
+ return result as ActivityInterfaceFor<ThreadOps>;
70
+ }
71
+
54
72
  function createMockThreadOps() {
55
73
  const log: { op: string; args: unknown[] }[] = [];
56
- const ops: ThreadOps = {
74
+ const ops = toActivityInterface({
57
75
  initializeThread: async (threadId) => {
58
76
  log.push({ op: "initializeThread", args: [threadId] });
59
77
  },
@@ -69,7 +87,7 @@ function createMockThreadOps() {
69
87
  forkThread: async (source, target) => {
70
88
  log.push({ op: "forkThread", args: [source, target] });
71
89
  },
72
- };
90
+ });
73
91
  return { ops, log };
74
92
  }
75
93
 
@@ -5,6 +5,7 @@ import type { ThreadOps } from "./types";
5
5
  import type { RunAgentActivity } from "../model/types";
6
6
  import type { RawToolCall } from "../tool-router/types";
7
7
  import type { SandboxOps } from "../sandbox/types";
8
+ import type { ActivityInterfaceFor } from "@temporalio/workflow";
8
9
 
9
10
  // ---------------------------------------------------------------------------
10
11
  // Mock @temporalio/workflow
@@ -58,10 +59,23 @@ function at<T>(arr: T[], index: number): T {
58
59
  return val;
59
60
  }
60
61
 
62
+ function toActivityInterface(
63
+ raw: ThreadOps,
64
+ ): ActivityInterfaceFor<ThreadOps> {
65
+ const result = {} as Record<string, unknown>;
66
+ for (const [key, fn] of Object.entries(raw)) {
67
+ const wrapped = (...args: unknown[]) => (fn as (...a: unknown[]) => unknown)(...args);
68
+ wrapped.executeWithOptions = (_opts: unknown, args: unknown[]) =>
69
+ (fn as (...a: unknown[]) => unknown)(...args);
70
+ result[key] = wrapped;
71
+ }
72
+ return result as ActivityInterfaceFor<ThreadOps>;
73
+ }
74
+
61
75
  function createMockThreadOps() {
62
76
  const log: { op: string; args: unknown[] }[] = [];
63
77
 
64
- const ops: ThreadOps = {
78
+ const ops = toActivityInterface({
65
79
  initializeThread: async (threadId) => {
66
80
  log.push({ op: "initializeThread", args: [threadId] });
67
81
  },
@@ -77,7 +91,7 @@ function createMockThreadOps() {
77
91
  forkThread: async (source, target) => {
78
92
  log.push({ op: "forkThread", args: [source, target] });
79
93
  },
80
- };
94
+ });
81
95
 
82
96
  return { ops, log };
83
97
  }
@@ -4,6 +4,7 @@ import {
4
4
  defineUpdate,
5
5
  setHandler,
6
6
  ApplicationFailure,
7
+ type ActivityInterfaceFor,
7
8
  } from "@temporalio/workflow";
8
9
  import type { SessionExitReason, MessageContent } from "../types";
9
10
  import type { ThreadOps, SessionConfig, ZeitlichSession } from "./types";
@@ -14,6 +15,7 @@ import type { ParsedToolCallUnion, ToolMap } from "../tool-router/types";
14
15
  import { getShortId } from "../thread/id";
15
16
  import { buildSubagentRegistration } from "../subagent/register";
16
17
  import { buildSkillRegistration } from "../skills/register";
18
+ import { uuid4 } from "@temporalio/workflow";
17
19
 
18
20
  /**
19
21
  * Creates an agent session that manages the agent loop: LLM invocation,
@@ -133,7 +135,7 @@ export const createSession = async <T extends ToolMap, M = unknown>({
133
135
  threadId,
134
136
  });
135
137
  }
136
- await appendHumanMessage(threadId, message);
138
+ await appendHumanMessage(threadId, uuid4(), message);
137
139
  if (hooks.onPostHumanMessageAppend) {
138
140
  await hooks.onPostHumanMessageAppend({
139
141
  message,
@@ -175,12 +177,12 @@ export const createSession = async <T extends ToolMap, M = unknown>({
175
177
  nonRetryable: true,
176
178
  });
177
179
  }
178
- await appendSystemMessage(threadId, systemPrompt);
180
+ await appendSystemMessage(threadId, uuid4(), systemPrompt);
179
181
  } else {
180
182
  await initializeThread(threadId);
181
183
  }
182
184
  }
183
- await appendHumanMessage(threadId, await buildContextMessage());
185
+ await appendHumanMessage(threadId, uuid4(), await buildContextMessage());
184
186
 
185
187
  let exitReason: SessionExitReason = "completed";
186
188
 
@@ -221,7 +223,7 @@ export const createSession = async <T extends ToolMap, M = unknown>({
221
223
  try {
222
224
  parsedToolCalls.push(toolRouter.parseToolCall(tc));
223
225
  } catch (error) {
224
- await appendToolResult({
226
+ await appendToolResult(uuid4(), {
225
227
  threadId,
226
228
  toolCallId: tc.id ?? "",
227
229
  toolName: tc.name,
@@ -299,7 +301,7 @@ export const createSession = async <T extends ToolMap, M = unknown>({
299
301
  */
300
302
  export function proxyDefaultThreadOps(
301
303
  options?: Parameters<typeof proxyActivities>[0]
302
- ): ThreadOps {
304
+ ): ActivityInterfaceFor<ThreadOps> {
303
305
  return proxyActivities<ThreadOps>(
304
306
  options ?? {
305
307
  startToCloseTimeout: "10s",
@@ -15,6 +15,7 @@ import type { Skill } from "../skills/types";
15
15
  import type { SandboxOps } from "../sandbox/types";
16
16
  import type { RunAgentActivity } from "../model/types";
17
17
  import type { AgentStateManager, JsonSerializable } from "../state/types";
18
+ import type { ActivityInterfaceFor } from "@temporalio/workflow";
18
19
 
19
20
  /**
20
21
  * Thread operations required by a session.
@@ -26,12 +27,17 @@ export interface ThreadOps {
26
27
  /** Append a human message to the thread */
27
28
  appendHumanMessage(
28
29
  threadId: string,
30
+ id: string,
29
31
  content: string | MessageContent
30
32
  ): Promise<void>;
31
33
  /** Append a tool result to the thread */
32
- appendToolResult(config: ToolResultConfig): Promise<void>;
34
+ appendToolResult(id: string, config: ToolResultConfig): Promise<void>;
33
35
  /** Append a system message to the thread */
34
- appendSystemMessage(threadId: string, content: string): Promise<void>;
36
+ appendSystemMessage(
37
+ threadId: string,
38
+ id: string,
39
+ content: string
40
+ ): Promise<void>;
35
41
  /** Copy all messages from sourceThreadId into a new thread at targetThreadId */
36
42
  forkThread(sourceThreadId: string, targetThreadId: string): Promise<void>;
37
43
  }
@@ -53,7 +59,7 @@ export interface SessionConfig<T extends ToolMap, M = unknown> {
53
59
  /** Workflow-specific runAgent activity (with tools pre-bound) */
54
60
  runAgent: RunAgentActivity<M>;
55
61
  /** Thread operations (initialize, append messages, parse tool calls) */
56
- threadOps?: ThreadOps;
62
+ threadOps?: ActivityInterfaceFor<ThreadOps>;
57
63
  /** Tool router for processing tool calls (optional if agent has no tools) */
58
64
  tools?: T;
59
65
  /** Subagent configurations */
@@ -89,7 +89,7 @@ export function createSubagentHandler<
89
89
  if (config.allowThreadContinuation && childThreadId) {
90
90
  finalToolResponse =
91
91
  typeof toolResponse === "string"
92
- ? `${toolResponse}\n\n[Thread ID: ${childThreadId}]`
92
+ ? `${toolResponse}\n\n[${config.agentName} Thread ID: ${childThreadId}]`
93
93
  : toolResponse;
94
94
  }
95
95
 
@@ -476,13 +476,14 @@ describe("buildSubagentRegistration", () => {
476
476
 
477
477
  expect(reg).toBeDefined();
478
478
  if (reg) {
479
- expect(reg.description).toContain("Agent A");
480
- expect(reg.description).toContain("Agent B");
479
+ const desc = reg.description as () => string;
480
+ expect(desc()).toContain("Agent A");
481
+ expect(desc()).toContain("Agent B");
481
482
 
482
483
  bEnabled = false;
483
484
 
484
- expect(reg.description).toContain("Agent A");
485
- expect(reg.description).not.toContain("Agent B");
485
+ expect(desc()).toContain("Agent A");
486
+ expect(desc()).not.toContain("Agent B");
486
487
  }
487
488
  });
488
489
  });
@@ -57,7 +57,7 @@ export function createSubagentTool<T extends SubagentConfig[]>(
57
57
  .string()
58
58
  .nullable()
59
59
  .describe(
60
- "Thread ID to continue an existing conversation, or null to start a new one"
60
+ "Thread ID to continue an existing conversation from the same subagent, or null to start a new one"
61
61
  ),
62
62
  })
63
63
  : z.object(baseFields);
@@ -1,5 +1,4 @@
1
1
  export { createThreadManager } from "./manager";
2
- export { getShortId } from "./id";
3
2
 
4
3
  export type {
5
4
  ThreadManagerConfig,
@@ -12,13 +12,13 @@ import type { Sandbox } from "../sandbox/types";
12
12
  describe("withAutoAppend", () => {
13
13
  it("appends tool result via threadHandler and sets resultAppended", async () => {
14
14
  const appended: ToolResultConfig[] = [];
15
- const threadHandler = async (config: ToolResultConfig) => {
15
+ const threadHandler = async (_id: string, config: ToolResultConfig) => {
16
16
  appended.push(config);
17
17
  };
18
18
 
19
19
  const innerHandler = async (
20
20
  args: { text: string },
21
- _ctx: RouterContext,
21
+ _ctx: RouterContext
22
22
  ): Promise<ToolHandlerResponse<{ echoed: string }>> => ({
23
23
  toolResponse: `Echo: ${args.text}`,
24
24
  data: { echoed: args.text },
@@ -32,7 +32,7 @@ describe("withAutoAppend", () => {
32
32
  threadId: "thread-1",
33
33
  toolCallId: "tc-1",
34
34
  toolName: "Echo",
35
- },
35
+ }
36
36
  );
37
37
 
38
38
  expect(result.resultAppended).toBe(true);
@@ -62,7 +62,7 @@ describe("withAutoAppend", () => {
62
62
 
63
63
  const result = await wrapped(
64
64
  {},
65
- { threadId: "t", toolCallId: "tc", toolName: "BigTool" },
65
+ { threadId: "t", toolCallId: "tc", toolName: "BigTool" }
66
66
  );
67
67
 
68
68
  expect(result.toolResponse).toBe("Response appended via withAutoAppend");
@@ -81,10 +81,7 @@ describe("withAutoAppend", () => {
81
81
  const wrapped = withAutoAppend(threadHandler, innerHandler);
82
82
 
83
83
  await expect(
84
- wrapped(
85
- {},
86
- { threadId: "t", toolCallId: "tc", toolName: "Fail" },
87
- ),
84
+ wrapped({}, { threadId: "t", toolCallId: "tc", toolName: "Fail" })
88
85
  ).rejects.toThrow("handler failed");
89
86
 
90
87
  expect(appendSpy).not.toHaveBeenCalled();
@@ -92,7 +89,7 @@ describe("withAutoAppend", () => {
92
89
 
93
90
  it("uses correct context fields for thread handler config", async () => {
94
91
  let capturedConfig: ToolResultConfig | null = null;
95
- const threadHandler = async (config: ToolResultConfig) => {
92
+ const threadHandler = async (_id: string, config: ToolResultConfig) => {
96
93
  capturedConfig = config;
97
94
  };
98
95
 
@@ -110,7 +107,7 @@ describe("withAutoAppend", () => {
110
107
  toolCallId: "my-tc",
111
108
  toolName: "MyTool",
112
109
  sandboxId: "sb-1",
113
- },
110
+ }
114
111
  );
115
112
 
116
113
  expect(capturedConfig).toEqual({
@@ -177,7 +174,7 @@ describe("withSandbox", () => {
177
174
 
178
175
  const handler = async (
179
176
  _args: { text: string },
180
- ctx: RouterContext & { sandbox: Sandbox; sandboxId: string },
177
+ ctx: RouterContext & { sandbox: Sandbox; sandboxId: string }
181
178
  ): Promise<ToolHandlerResponse<null>> => {
182
179
  capturedSandbox = ctx.sandbox;
183
180
  capturedSandboxId = ctx.sandboxId;
@@ -193,7 +190,7 @@ describe("withSandbox", () => {
193
190
  toolCallId: "tc-1",
194
191
  toolName: "Test",
195
192
  sandboxId: "sb-42",
196
- },
193
+ }
197
194
  );
198
195
 
199
196
  expect(result.toolResponse).toBe("ok");
@@ -219,7 +216,7 @@ describe("withSandbox", () => {
219
216
  threadId: "thread-1",
220
217
  toolCallId: "tc-1",
221
218
  toolName: "Bash",
222
- },
219
+ }
223
220
  );
224
221
 
225
222
  expect(result.toolResponse).toContain("No sandbox configured");
@@ -247,7 +244,7 @@ describe("withSandbox", () => {
247
244
  toolCallId: "tc",
248
245
  toolName: "Grep",
249
246
  sandboxId: undefined,
250
- },
247
+ }
251
248
  );
252
249
 
253
250
  expect(result.toolResponse).toContain("No sandbox configured");
@@ -276,8 +273,8 @@ describe("withSandbox", () => {
276
273
  toolCallId: "tc",
277
274
  toolName: "Test",
278
275
  sandboxId: "sb-missing",
279
- },
280
- ),
276
+ }
277
+ )
281
278
  ).rejects.toThrow("sandbox not found");
282
279
  });
283
280
 
@@ -285,11 +282,13 @@ describe("withSandbox", () => {
285
282
  const mockSandbox = createMockSandbox();
286
283
  const manager = { getSandbox: async () => mockSandbox };
287
284
 
288
- let capturedCtx: (RouterContext & { sandbox: Sandbox; sandboxId: string }) | null = null;
285
+ let capturedCtx:
286
+ | (RouterContext & { sandbox: Sandbox; sandboxId: string })
287
+ | null = null;
289
288
 
290
289
  const handler = async (
291
290
  _args: unknown,
292
- ctx: RouterContext & { sandbox: Sandbox; sandboxId: string },
291
+ ctx: RouterContext & { sandbox: Sandbox; sandboxId: string }
293
292
  ): Promise<ToolHandlerResponse<null>> => {
294
293
  capturedCtx = ctx;
295
294
  return { toolResponse: "ok", data: null };
@@ -304,7 +303,7 @@ describe("withSandbox", () => {
304
303
  toolCallId: "my-tc",
305
304
  toolName: "MyTool",
306
305
  sandboxId: "my-sandbox",
307
- },
306
+ }
308
307
  );
309
308
 
310
309
  expect(capturedCtx).toEqual(
@@ -314,7 +313,7 @@ describe("withSandbox", () => {
314
313
  toolName: "MyTool",
315
314
  sandboxId: "my-sandbox",
316
315
  sandbox: mockSandbox,
317
- }),
316
+ })
318
317
  );
319
318
  });
320
319
 
@@ -335,7 +334,7 @@ describe("withSandbox", () => {
335
334
  toolCallId: "tc",
336
335
  toolName: "Test",
337
336
  sandboxId: "",
338
- },
337
+ }
339
338
  );
340
339
 
341
340
  expect(result.toolResponse).toContain("No sandbox configured");
@@ -1,5 +1,6 @@
1
1
  import type { ToolResultConfig } from "../types";
2
2
  import type { ActivityToolHandler, RouterContext } from "./types";
3
+ import { v4 as uuidv4 } from "uuid";
3
4
 
4
5
  /**
5
6
  * Wraps a tool handler to automatically append its result directly to the
@@ -33,13 +34,13 @@ export function withAutoAppend<
33
34
  TResult,
34
35
  TContext extends RouterContext = RouterContext,
35
36
  >(
36
- threadHandler: (config: ToolResultConfig) => Promise<void>,
37
+ threadHandler: (id: string, config: ToolResultConfig) => Promise<void>,
37
38
  handler: ActivityToolHandler<TArgs, TResult, TContext>
38
39
  ): ActivityToolHandler<TArgs, TResult, TContext> {
39
40
  return async (args: TArgs, context: TContext) => {
40
41
  const response = await handler(args, context);
41
42
 
42
- await threadHandler({
43
+ await threadHandler(uuidv4(), {
43
44
  threadId: context.threadId,
44
45
  toolCallId: context.toolCallId,
45
46
  toolName: context.toolName,
@@ -15,10 +15,7 @@ vi.mock("@temporalio/workflow", () => {
15
15
  err.nonRetryable = nonRetryable;
16
16
  return err;
17
17
  }
18
- static fromError(
19
- error: unknown,
20
- options?: { nonRetryable?: boolean },
21
- ) {
18
+ static fromError(error: unknown, options?: { nonRetryable?: boolean }) {
22
19
  const src = error instanceof Error ? error : new Error(String(error));
23
20
  const err = new MockApplicationFailure(src.message);
24
21
  err.nonRetryable = options?.nonRetryable;
@@ -29,18 +26,25 @@ vi.mock("@temporalio/workflow", () => {
29
26
  });
30
27
 
31
28
  import { createToolRouter, defineTool, hasNoOtherToolCalls } from "./router";
32
- import type {
33
- ToolMap,
34
- ToolHandlerResponse,
35
- AppendToolResultFn,
36
- } from "./types";
29
+ import type { ToolMap, ToolHandlerResponse, AppendToolResultFn } from "./types";
37
30
  import type { ToolResultConfig } from "../types";
38
31
 
39
32
  function createAppendSpy() {
40
33
  const calls: ToolResultConfig[] = [];
41
- const fn: AppendToolResultFn = async (config) => {
42
- calls.push(config);
43
- };
34
+ const fn = Object.assign(
35
+ async (_id: string, config: ToolResultConfig) => {
36
+ calls.push(config);
37
+ },
38
+ {
39
+ executeWithOptions: (
40
+ _opts: unknown,
41
+ [, config]: [string, ToolResultConfig]
42
+ ) => {
43
+ calls.push(config);
44
+ return Promise.resolve();
45
+ },
46
+ }
47
+ ) as AppendToolResultFn;
44
48
  return { fn, calls };
45
49
  }
46
50
 
@@ -146,7 +150,11 @@ describe("createToolRouter edge cases", () => {
146
150
  },
147
151
  });
148
152
 
149
- const parsed = router.parseToolCall({ id: "tc-1", name: "Hooked", args: {} });
153
+ const parsed = router.parseToolCall({
154
+ id: "tc-1",
155
+ name: "Hooked",
156
+ args: {},
157
+ });
150
158
  await router.processToolCalls([parsed], { turn: 1 });
151
159
 
152
160
  expect(order).toEqual(["global-pre", "tool-pre", "handler"]);
@@ -185,7 +193,11 @@ describe("createToolRouter edge cases", () => {
185
193
  },
186
194
  });
187
195
 
188
- const parsed = router.parseToolCall({ id: "tc-1", name: "Hooked", args: {} });
196
+ const parsed = router.parseToolCall({
197
+ id: "tc-1",
198
+ name: "Hooked",
199
+ args: {},
200
+ });
189
201
  await router.processToolCalls([parsed], { turn: 1 });
190
202
 
191
203
  expect(order).toEqual(["global-pre-skip"]);
@@ -219,7 +231,11 @@ describe("createToolRouter edge cases", () => {
219
231
  },
220
232
  });
221
233
 
222
- const parsed = router.parseToolCall({ id: "tc-1", name: "Hooked", args: {} });
234
+ const parsed = router.parseToolCall({
235
+ id: "tc-1",
236
+ name: "Hooked",
237
+ args: {},
238
+ });
223
239
  await router.processToolCalls([parsed], { turn: 1 });
224
240
 
225
241
  expect(order).toEqual(["tool-post", "global-post"]);
@@ -259,7 +275,10 @@ describe("createToolRouter edge cases", () => {
259
275
  const results = await router.processToolCalls([parsed], { turn: 1 });
260
276
 
261
277
  expect(at(appendSpy.calls, 0).content).toBe("tool-level recovery");
262
- expect(at(results, 0).data).toEqual({ error: "Error: boom", recovered: true });
278
+ expect(at(results, 0).data).toEqual({
279
+ error: "Error: boom",
280
+ recovered: true,
281
+ });
263
282
  expect(globalHookSpy).not.toHaveBeenCalled();
264
283
  });
265
284
 
@@ -365,7 +384,11 @@ describe("createToolRouter edge cases", () => {
365
384
  plugins: [pluginTool],
366
385
  });
367
386
 
368
- const parsed = router.parseToolCall({ id: "tc-1", name: "MyTool", args: {} });
387
+ const parsed = router.parseToolCall({
388
+ id: "tc-1",
389
+ name: "MyTool",
390
+ args: {},
391
+ });
369
392
  const results = await router.processToolCalls([parsed]);
370
393
 
371
394
  expect(at(results, 0).data).toEqual({ source: "plugin" });
@@ -393,7 +416,7 @@ describe("createToolRouter edge cases", () => {
393
416
  const results = await router.processToolCallsByName(
394
417
  [],
395
418
  "Echo",
396
- async () => ({ toolResponse: "ok", data: null }),
419
+ async () => ({ toolResponse: "ok", data: null })
397
420
  );
398
421
 
399
422
  expect(results).toEqual([]);
@@ -421,7 +444,11 @@ describe("createToolRouter edge cases", () => {
421
444
  appendToolResult: appendSpy.fn,
422
445
  });
423
446
 
424
- const parsed = router.parseToolCall({ id: "tc-1", name: "Complex", args: {} });
447
+ const parsed = router.parseToolCall({
448
+ id: "tc-1",
449
+ name: "Complex",
450
+ args: {},
451
+ });
425
452
  await router.processToolCalls([parsed]);
426
453
 
427
454
  const appended = at(appendSpy.calls, 0);
@@ -435,7 +462,9 @@ describe("createToolRouter edge cases", () => {
435
462
  name: "Sync" as const,
436
463
  description: "sync handler",
437
464
  schema: z.object({ n: z.number() }),
438
- handler: (args: { n: number }): ToolHandlerResponse<{ doubled: number }> => ({
465
+ handler: (args: {
466
+ n: number;
467
+ }): ToolHandlerResponse<{ doubled: number }> => ({
439
468
  toolResponse: `${args.n * 2}`,
440
469
  data: { doubled: args.n * 2 },
441
470
  }),
@@ -447,7 +476,11 @@ describe("createToolRouter edge cases", () => {
447
476
  appendToolResult: appendSpy.fn,
448
477
  });
449
478
 
450
- const parsed = router.parseToolCall({ id: "tc-1", name: "Sync", args: { n: 5 } });
479
+ const parsed = router.parseToolCall({
480
+ id: "tc-1",
481
+ name: "Sync",
482
+ args: { n: 5 },
483
+ });
451
484
  const results = await router.processToolCalls([parsed]);
452
485
 
453
486
  expect(at(results, 0).data).toEqual({ doubled: 10 });
@@ -504,7 +537,11 @@ describe("createToolRouter edge cases", () => {
504
537
  appendToolResult: appendSpy.fn,
505
538
  });
506
539
 
507
- const parsed = router.parseToolCall({ id: "tc-1", name: "Suppress", args: {} });
540
+ const parsed = router.parseToolCall({
541
+ id: "tc-1",
542
+ name: "Suppress",
543
+ args: {},
544
+ });
508
545
  const results = await router.processToolCalls([parsed], { turn: 1 });
509
546
 
510
547
  expect(at(results, 0).data).toEqual({
@@ -590,7 +627,11 @@ describe("createToolRouter edge cases", () => {
590
627
  },
591
628
  });
592
629
 
593
- const parsed = router.parseToolCall({ id: "tc-1", name: "ThrowString", args: {} });
630
+ const parsed = router.parseToolCall({
631
+ id: "tc-1",
632
+ name: "ThrowString",
633
+ args: {},
634
+ });
594
635
  const results = await router.processToolCalls([parsed], { turn: 1 });
595
636
 
596
637
  expect(at(results, 0).data).toEqual({