zeitlich 0.2.20 → 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 (46) hide show
  1. package/dist/adapters/sandbox/virtual/index.d.cts +3 -3
  2. package/dist/adapters/sandbox/virtual/index.d.ts +3 -3
  3. package/dist/adapters/thread/google-genai/index.cjs +70 -23
  4. package/dist/adapters/thread/google-genai/index.cjs.map +1 -1
  5. package/dist/adapters/thread/google-genai/index.d.cts +10 -10
  6. package/dist/adapters/thread/google-genai/index.d.ts +10 -10
  7. package/dist/adapters/thread/google-genai/index.js +70 -23
  8. package/dist/adapters/thread/google-genai/index.js.map +1 -1
  9. package/dist/adapters/thread/langchain/index.cjs +75 -70
  10. package/dist/adapters/thread/langchain/index.cjs.map +1 -1
  11. package/dist/adapters/thread/langchain/index.d.cts +10 -10
  12. package/dist/adapters/thread/langchain/index.d.ts +10 -10
  13. package/dist/adapters/thread/langchain/index.js +75 -70
  14. package/dist/adapters/thread/langchain/index.js.map +1 -1
  15. package/dist/index.cjs +54 -9
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +6 -6
  18. package/dist/index.d.ts +6 -6
  19. package/dist/index.js +54 -9
  20. package/dist/index.js.map +1 -1
  21. package/dist/{queries-KHj5Otv7.d.ts → queries-6Avfh74U.d.ts} +1 -1
  22. package/dist/{queries-nIdzTCDS.d.cts → queries-CHa2iv_I.d.cts} +1 -1
  23. package/dist/{types-By80IE1x.d.ts → types-BkAYmc96.d.ts} +4 -4
  24. package/dist/{types-DZ7BkA3-.d.cts → types-CES_30qx.d.cts} +4 -4
  25. package/dist/{types-Ct2igz9y.d.ts → types-YbL7JpEA.d.cts} +1 -1
  26. package/dist/{types-Ct2igz9y.d.cts → types-YbL7JpEA.d.ts} +1 -1
  27. package/dist/workflow.cjs +7 -8
  28. package/dist/workflow.cjs.map +1 -1
  29. package/dist/workflow.d.cts +6 -6
  30. package/dist/workflow.d.ts +6 -6
  31. package/dist/workflow.js +7 -8
  32. package/dist/workflow.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/adapters/thread/google-genai/activities.ts +11 -9
  35. package/src/adapters/thread/google-genai/model-invoker.ts +6 -11
  36. package/src/adapters/thread/google-genai/thread-manager.ts +44 -29
  37. package/src/adapters/thread/langchain/activities.ts +6 -4
  38. package/src/adapters/thread/langchain/thread-manager.ts +46 -22
  39. package/src/lib/session/session.ts +5 -4
  40. package/src/lib/session/types.ts +7 -2
  41. package/src/lib/tool-router/auto-append-sandbox.integration.test.ts +20 -21
  42. package/src/lib/tool-router/auto-append.ts +3 -2
  43. package/src/lib/tool-router/router-edge-cases.integration.test.ts +59 -23
  44. package/src/lib/tool-router/router.integration.test.ts +55 -23
  45. package/src/lib/tool-router/router.ts +4 -3
  46. package/src/lib/tool-router/types.ts +1 -1
@@ -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,22 +26,24 @@ 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
34
  const fn = Object.assign(
42
- async (config: ToolResultConfig) => { calls.push(config); },
43
- { executeWithOptions: (_opts: unknown, [config]: [ToolResultConfig]) => {
35
+ async (_id: string, config: ToolResultConfig) => {
36
+ calls.push(config);
37
+ },
38
+ {
39
+ executeWithOptions: (
40
+ _opts: unknown,
41
+ [, config]: [string, ToolResultConfig]
42
+ ) => {
44
43
  calls.push(config);
45
44
  return Promise.resolve();
46
45
  },
47
- },
46
+ }
48
47
  ) as AppendToolResultFn;
49
48
  return { fn, calls };
50
49
  }
@@ -151,7 +150,11 @@ describe("createToolRouter edge cases", () => {
151
150
  },
152
151
  });
153
152
 
154
- 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
+ });
155
158
  await router.processToolCalls([parsed], { turn: 1 });
156
159
 
157
160
  expect(order).toEqual(["global-pre", "tool-pre", "handler"]);
@@ -190,7 +193,11 @@ describe("createToolRouter edge cases", () => {
190
193
  },
191
194
  });
192
195
 
193
- 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
+ });
194
201
  await router.processToolCalls([parsed], { turn: 1 });
195
202
 
196
203
  expect(order).toEqual(["global-pre-skip"]);
@@ -224,7 +231,11 @@ describe("createToolRouter edge cases", () => {
224
231
  },
225
232
  });
226
233
 
227
- 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
+ });
228
239
  await router.processToolCalls([parsed], { turn: 1 });
229
240
 
230
241
  expect(order).toEqual(["tool-post", "global-post"]);
@@ -264,7 +275,10 @@ describe("createToolRouter edge cases", () => {
264
275
  const results = await router.processToolCalls([parsed], { turn: 1 });
265
276
 
266
277
  expect(at(appendSpy.calls, 0).content).toBe("tool-level recovery");
267
- 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
+ });
268
282
  expect(globalHookSpy).not.toHaveBeenCalled();
269
283
  });
270
284
 
@@ -370,7 +384,11 @@ describe("createToolRouter edge cases", () => {
370
384
  plugins: [pluginTool],
371
385
  });
372
386
 
373
- 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
+ });
374
392
  const results = await router.processToolCalls([parsed]);
375
393
 
376
394
  expect(at(results, 0).data).toEqual({ source: "plugin" });
@@ -398,7 +416,7 @@ describe("createToolRouter edge cases", () => {
398
416
  const results = await router.processToolCallsByName(
399
417
  [],
400
418
  "Echo",
401
- async () => ({ toolResponse: "ok", data: null }),
419
+ async () => ({ toolResponse: "ok", data: null })
402
420
  );
403
421
 
404
422
  expect(results).toEqual([]);
@@ -426,7 +444,11 @@ describe("createToolRouter edge cases", () => {
426
444
  appendToolResult: appendSpy.fn,
427
445
  });
428
446
 
429
- 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
+ });
430
452
  await router.processToolCalls([parsed]);
431
453
 
432
454
  const appended = at(appendSpy.calls, 0);
@@ -440,7 +462,9 @@ describe("createToolRouter edge cases", () => {
440
462
  name: "Sync" as const,
441
463
  description: "sync handler",
442
464
  schema: z.object({ n: z.number() }),
443
- handler: (args: { n: number }): ToolHandlerResponse<{ doubled: number }> => ({
465
+ handler: (args: {
466
+ n: number;
467
+ }): ToolHandlerResponse<{ doubled: number }> => ({
444
468
  toolResponse: `${args.n * 2}`,
445
469
  data: { doubled: args.n * 2 },
446
470
  }),
@@ -452,7 +476,11 @@ describe("createToolRouter edge cases", () => {
452
476
  appendToolResult: appendSpy.fn,
453
477
  });
454
478
 
455
- 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
+ });
456
484
  const results = await router.processToolCalls([parsed]);
457
485
 
458
486
  expect(at(results, 0).data).toEqual({ doubled: 10 });
@@ -509,7 +537,11 @@ describe("createToolRouter edge cases", () => {
509
537
  appendToolResult: appendSpy.fn,
510
538
  });
511
539
 
512
- 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
+ });
513
545
  const results = await router.processToolCalls([parsed], { turn: 1 });
514
546
 
515
547
  expect(at(results, 0).data).toEqual({
@@ -595,7 +627,11 @@ describe("createToolRouter edge cases", () => {
595
627
  },
596
628
  });
597
629
 
598
- 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
+ });
599
635
  const results = await router.processToolCalls([parsed], { turn: 1 });
600
636
 
601
637
  expect(at(results, 0).data).toEqual({
@@ -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;
@@ -45,7 +42,9 @@ const echoTool = defineTool({
45
42
  name: "Echo" as const,
46
43
  description: "Echoes back the input",
47
44
  schema: z.object({ text: z.string() }),
48
- handler: async (args: { text: string }): Promise<ToolHandlerResponse<{ echoed: string }>> => ({
45
+ handler: async (args: {
46
+ text: string;
47
+ }): Promise<ToolHandlerResponse<{ echoed: string }>> => ({
49
48
  toolResponse: `Echo: ${args.text}`,
50
49
  data: { echoed: args.text },
51
50
  }),
@@ -55,7 +54,10 @@ const mathTool = defineTool({
55
54
  name: "Add" as const,
56
55
  description: "Adds two numbers",
57
56
  schema: z.object({ a: z.number(), b: z.number() }),
58
- handler: async (args: { a: number; b: number }): Promise<ToolHandlerResponse<{ sum: number }>> => ({
57
+ handler: async (args: {
58
+ a: number;
59
+ b: number;
60
+ }): Promise<ToolHandlerResponse<{ sum: number }>> => ({
59
61
  toolResponse: `Sum: ${args.a + args.b}`,
60
62
  data: { sum: args.a + args.b },
61
63
  }),
@@ -65,7 +67,9 @@ const failingTool = defineTool({
65
67
  name: "Fail" as const,
66
68
  description: "Always fails",
67
69
  schema: z.object({ reason: z.string() }),
68
- handler: async (args: { reason: string }): Promise<ToolHandlerResponse<null>> => {
70
+ handler: async (args: {
71
+ reason: string;
72
+ }): Promise<ToolHandlerResponse<null>> => {
69
73
  throw new Error(args.reason);
70
74
  },
71
75
  });
@@ -87,12 +91,18 @@ function createTools() {
87
91
  function createAppendSpy() {
88
92
  const calls: ToolResultConfig[] = [];
89
93
  const fn = Object.assign(
90
- async (config: ToolResultConfig) => { calls.push(config); },
91
- { executeWithOptions: (_opts: unknown, [config]: [ToolResultConfig]) => {
94
+ async (_id: string, config: ToolResultConfig) => {
95
+ calls.push(config);
96
+ },
97
+ {
98
+ executeWithOptions: (
99
+ _opts: unknown,
100
+ [, config]: [string, ToolResultConfig]
101
+ ) => {
92
102
  calls.push(config);
93
103
  return Promise.resolve();
94
104
  },
95
- },
105
+ }
96
106
  ) as AppendToolResultFn;
97
107
  return { fn, calls };
98
108
  }
@@ -168,7 +178,7 @@ describe("createToolRouter integration", () => {
168
178
  });
169
179
 
170
180
  expect(() =>
171
- router.parseToolCall({ id: "tc-1", name: "Unknown", args: {} }),
181
+ router.parseToolCall({ id: "tc-1", name: "Unknown", args: {} })
172
182
  ).toThrow("Tool Unknown not found");
173
183
  });
174
184
 
@@ -180,7 +190,7 @@ describe("createToolRouter integration", () => {
180
190
  });
181
191
 
182
192
  expect(() =>
183
- router.parseToolCall({ id: "tc-1", name: "Echo", args: { text: 123 } }),
193
+ router.parseToolCall({ id: "tc-1", name: "Echo", args: { text: 123 } })
184
194
  ).toThrow();
185
195
  });
186
196
 
@@ -329,7 +339,7 @@ describe("createToolRouter integration", () => {
329
339
  toolCallId: "tc-99",
330
340
  toolName: "Spy",
331
341
  sandboxId: "sandbox-1",
332
- }),
342
+ })
333
343
  );
334
344
  });
335
345
 
@@ -357,12 +367,18 @@ describe("createToolRouter integration", () => {
357
367
  },
358
368
  });
359
369
 
360
- const parsed = router.parseToolCall({ id: "tc-1", name: "Skippable", args: {} });
370
+ const parsed = router.parseToolCall({
371
+ id: "tc-1",
372
+ name: "Skippable",
373
+ args: {},
374
+ });
361
375
  const results = await router.processToolCalls([parsed], { turn: 1 });
362
376
 
363
377
  expect(handlerSpy).not.toHaveBeenCalled();
364
378
  expect(results).toHaveLength(0);
365
- expect(at(appendSpy.calls, 0).content).toContain("Skipped by PreToolUse hook");
379
+ expect(at(appendSpy.calls, 0).content).toContain(
380
+ "Skipped by PreToolUse hook"
381
+ );
366
382
  });
367
383
 
368
384
  it("pre-hook can modify arguments", async () => {
@@ -421,7 +437,11 @@ describe("createToolRouter integration", () => {
421
437
  appendToolResult: appendSpy.fn,
422
438
  });
423
439
 
424
- const parsed = router.parseToolCall({ id: "tc-1", name: "Hooked", args: {} });
440
+ const parsed = router.parseToolCall({
441
+ id: "tc-1",
442
+ name: "Hooked",
443
+ args: {},
444
+ });
425
445
  const results = await router.processToolCalls([parsed], { turn: 1 });
426
446
 
427
447
  expect(handlerSpy).not.toHaveBeenCalled();
@@ -450,7 +470,10 @@ describe("createToolRouter integration", () => {
450
470
  await router.processToolCalls([parsed], { turn: 1 });
451
471
 
452
472
  expect(hookData).not.toBeNull();
453
- const data = hookData as unknown as { result: { data: unknown }; durationMs: number };
473
+ const data = hookData as unknown as {
474
+ result: { data: unknown };
475
+ durationMs: number;
476
+ };
454
477
  expect(data.result.data).toEqual({ sum: 7 });
455
478
  expect(data.durationMs).toBeGreaterThanOrEqual(0);
456
479
  });
@@ -511,7 +534,10 @@ describe("createToolRouter integration", () => {
511
534
  const results = await router.processToolCalls([parsed], { turn: 1 });
512
535
 
513
536
  expect(results).toHaveLength(1);
514
- expect(at(results, 0).data).toEqual({ error: "Error: boom", recovered: true });
537
+ expect(at(results, 0).data).toEqual({
538
+ error: "Error: boom",
539
+ recovered: true,
540
+ });
515
541
  expect(at(appendSpy.calls, 0).content).toBe("recovered gracefully");
516
542
  });
517
543
 
@@ -520,7 +546,9 @@ describe("createToolRouter integration", () => {
520
546
  name: "Fail" as const,
521
547
  description: "fails but suppresses",
522
548
  schema: z.object({ reason: z.string() }),
523
- handler: async (args: { reason: string }): Promise<ToolHandlerResponse<null>> => {
549
+ handler: async (args: {
550
+ reason: string;
551
+ }): Promise<ToolHandlerResponse<null>> => {
524
552
  throw new Error(args.reason);
525
553
  },
526
554
  hooks: {
@@ -562,7 +590,7 @@ describe("createToolRouter integration", () => {
562
590
  });
563
591
 
564
592
  await expect(
565
- router.processToolCalls([parsed], { turn: 1 }),
593
+ router.processToolCalls([parsed], { turn: 1 })
566
594
  ).rejects.toThrow("unrecoverable");
567
595
  });
568
596
 
@@ -586,7 +614,7 @@ describe("createToolRouter integration", () => {
586
614
  expect(router.hasTool("Disabled")).toBe(false);
587
615
  expect(router.getToolNames()).not.toContain("Disabled");
588
616
  expect(() =>
589
- router.parseToolCall({ id: "tc-1", name: "Disabled", args: {} }),
617
+ router.parseToolCall({ id: "tc-1", name: "Disabled", args: {} })
590
618
  ).toThrow("Tool Disabled not found");
591
619
  });
592
620
 
@@ -643,7 +671,7 @@ describe("createToolRouter integration", () => {
643
671
  async (args: { text: string }) => ({
644
672
  toolResponse: `custom: ${args.text}`,
645
673
  data: { custom: args.text },
646
- }),
674
+ })
647
675
  );
648
676
 
649
677
  expect(results).toHaveLength(2);
@@ -696,7 +724,11 @@ describe("createToolRouter integration", () => {
696
724
  appendToolResult: appendSpy.fn,
697
725
  });
698
726
 
699
- const parsed = router.parseToolCall({ id: "tc-1", name: "SelfAppend", args: {} });
727
+ const parsed = router.parseToolCall({
728
+ id: "tc-1",
729
+ name: "SelfAppend",
730
+ args: {},
731
+ });
700
732
  await router.processToolCalls([parsed]);
701
733
 
702
734
  expect(appendSpy.calls).toHaveLength(0);
@@ -20,7 +20,7 @@ import type {
20
20
  } from "./types";
21
21
 
22
22
  import type { z } from "zod";
23
- import { ApplicationFailure } from "@temporalio/workflow";
23
+ import { ApplicationFailure, uuid4 } from "@temporalio/workflow";
24
24
 
25
25
  /**
26
26
  * Creates a tool router for declarative tool call processing.
@@ -203,7 +203,7 @@ export function createToolRouter<T extends ToolMap>(
203
203
  // --- Pre-hooks: may skip or modify args ---
204
204
  const preResult = await runPreHooks(toolCall, tool, turn);
205
205
  if (preResult.skip) {
206
- await appendToolResult({
206
+ await appendToolResult(uuid4(), {
207
207
  threadId: options.threadId,
208
208
  toolCallId: toolCall.id,
209
209
  toolName: toolCall.name,
@@ -264,7 +264,7 @@ export function createToolRouter<T extends ToolMap>(
264
264
  {
265
265
  summary: `Append ${toolCall.name} result`,
266
266
  },
267
- [config]
267
+ [uuid4(), config]
268
268
  );
269
269
  }
270
270
 
@@ -395,6 +395,7 @@ export function createToolRouter<T extends ToolMap>(
395
395
  summary: `Append ${toolCall.name} result`,
396
396
  },
397
397
  [
398
+ uuid4(),
398
399
  {
399
400
  threadId: options.threadId,
400
401
  toolCallId: toolCall.id,
@@ -116,7 +116,7 @@ export type ParsedToolCallUnion<T extends ToolMap> = {
116
116
  * Function signature for appending tool results to a thread.
117
117
  */
118
118
  export type AppendToolResultFn = ActivityFunctionWithOptions<
119
- (config: ToolResultConfig) => Promise<void>
119
+ (id: string, config: ToolResultConfig) => Promise<void>
120
120
  >;
121
121
 
122
122
  /**