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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zeitlich",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "description": "[EXPERIMENTAL] An opinionated AI agent implementation for Temporal",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -68,7 +68,7 @@ export interface GoogleGenAIAdapter {
68
68
  * ```
69
69
  */
70
70
  export function createGoogleGenAIAdapter(
71
- config: GoogleGenAIAdapterConfig,
71
+ config: GoogleGenAIAdapterConfig
72
72
  ): GoogleGenAIAdapter {
73
73
  const { redis, client } = config;
74
74
 
@@ -80,29 +80,31 @@ export function createGoogleGenAIAdapter(
80
80
 
81
81
  async appendHumanMessage(
82
82
  threadId: string,
83
- content: string | MessageContent,
83
+ id: string,
84
+ content: string | MessageContent
84
85
  ): Promise<void> {
85
86
  const thread = createGoogleGenAIThreadManager({ redis, threadId });
86
- await thread.appendUserMessage(content);
87
+ await thread.appendUserMessage(id, content);
87
88
  },
88
89
 
89
90
  async appendSystemMessage(
90
91
  threadId: string,
91
- content: string,
92
+ id: string,
93
+ content: string
92
94
  ): Promise<void> {
93
95
  const thread = createGoogleGenAIThreadManager({ redis, threadId });
94
- await thread.appendSystemMessage(content);
96
+ await thread.appendSystemMessage(id, content);
95
97
  },
96
98
 
97
- async appendToolResult(cfg: ToolResultConfig): Promise<void> {
99
+ async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
98
100
  const { threadId, toolCallId, toolName, content } = cfg;
99
101
  const thread = createGoogleGenAIThreadManager({ redis, threadId });
100
- await thread.appendToolResult(toolCallId, toolName, content);
102
+ await thread.appendToolResult(id, toolCallId, toolName, content);
101
103
  },
102
104
 
103
105
  async forkThread(
104
106
  sourceThreadId: string,
105
- targetThreadId: string,
107
+ targetThreadId: string
106
108
  ): Promise<void> {
107
109
  const thread = createGoogleGenAIThreadManager({
108
110
  redis,
@@ -120,7 +122,7 @@ export function createGoogleGenAIAdapter(
120
122
  : ((() => {
121
123
  throw new Error(
122
124
  "No default model provided to createGoogleGenAIAdapter. " +
123
- "Either pass `model` in the config or use `createModelInvoker(model)` instead.",
125
+ "Either pass `model` in the config or use `createModelInvoker(model)` instead."
124
126
  );
125
127
  }) as unknown as ModelInvoker<Content>);
126
128
 
@@ -1,13 +1,10 @@
1
1
  import type Redis from "ioredis";
2
- import type {
3
- GoogleGenAI,
4
- Content,
5
- FunctionDeclaration,
6
- } from "@google/genai";
2
+ import type { GoogleGenAI, Content, FunctionDeclaration } from "@google/genai";
7
3
  import type { SerializableToolDefinition } from "../../../lib/types";
8
4
  import type { AgentResponse } from "../../../lib/model";
9
5
  import type { ModelInvokerConfig } from "../../../lib/model";
10
6
  import { createGoogleGenAIThreadManager } from "./thread-manager";
7
+ import { v4 as uuidv4 } from "uuid";
11
8
 
12
9
  export interface GoogleGenAIModelInvokerConfig {
13
10
  redis: Redis;
@@ -16,7 +13,7 @@ export interface GoogleGenAIModelInvokerConfig {
16
13
  }
17
14
 
18
15
  function toFunctionDeclarations(
19
- tools: SerializableToolDefinition[],
16
+ tools: SerializableToolDefinition[]
20
17
  ): FunctionDeclaration[] {
21
18
  return tools.map((t) => ({
22
19
  name: t.name,
@@ -73,7 +70,7 @@ export function createGoogleGenAIModelInvoker({
73
70
  model,
74
71
  }: GoogleGenAIModelInvokerConfig) {
75
72
  return async function invokeGoogleGenAIModel(
76
- config: ModelInvokerConfig,
73
+ config: ModelInvokerConfig
77
74
  ): Promise<AgentResponse<Content>> {
78
75
  const { threadId, state } = config;
79
76
 
@@ -97,9 +94,7 @@ export function createGoogleGenAIModelInvoker({
97
94
 
98
95
  const functionDeclarations = toFunctionDeclarations(state.tools);
99
96
  const tools =
100
- functionDeclarations.length > 0
101
- ? [{ functionDeclarations }]
102
- : undefined;
97
+ functionDeclarations.length > 0 ? [{ functionDeclarations }] : undefined;
103
98
 
104
99
  const response = await client.models.generateContent({
105
100
  model,
@@ -113,7 +108,7 @@ export function createGoogleGenAIModelInvoker({
113
108
  const responseParts = response.candidates?.[0]?.content?.parts ?? [];
114
109
  const modelContent: Content = { role: "model", parts: responseParts };
115
110
 
116
- await thread.appendModelContent(responseParts);
111
+ await thread.appendModelContent(uuidv4(), responseParts);
117
112
 
118
113
  const functionCalls = response.functionCalls ?? [];
119
114
 
@@ -21,23 +21,30 @@ export interface GoogleGenAIThreadManagerConfig {
21
21
  }
22
22
 
23
23
  /** Thread manager with Google GenAI Content convenience helpers */
24
- export interface GoogleGenAIThreadManager
25
- extends BaseThreadManager<StoredContent> {
26
- createUserContent(content: string | MessageContent): StoredContent;
27
- createSystemContent(content: string): StoredContent;
28
- createModelContent(parts: Part[]): StoredContent;
24
+ export interface GoogleGenAIThreadManager extends BaseThreadManager<StoredContent> {
25
+ createUserContent(
26
+ id: string,
27
+ content: string | MessageContent
28
+ ): StoredContent;
29
+ createSystemContent(id: string, content: string): StoredContent;
30
+ createModelContent(id: string, parts: Part[]): StoredContent;
29
31
  createToolResponseContent(
32
+ id: string,
30
33
  toolCallId: string,
31
34
  toolName: string,
32
- content: ToolMessageContent,
35
+ content: ToolMessageContent
33
36
  ): StoredContent;
34
- appendUserMessage(content: string | MessageContent): Promise<void>;
35
- appendSystemMessage(content: string): Promise<void>;
36
- appendModelContent(parts: Part[]): Promise<void>;
37
+ appendUserMessage(
38
+ id: string,
39
+ content: string | MessageContent
40
+ ): Promise<void>;
41
+ appendSystemMessage(id: string, content: string): Promise<void>;
42
+ appendModelContent(id: string, parts: Part[]): Promise<void>;
37
43
  appendToolResult(
44
+ id: string,
38
45
  toolCallId: string,
39
46
  toolName: string,
40
- content: ToolMessageContent,
47
+ content: ToolMessageContent
41
48
  ): Promise<void>;
42
49
  }
43
50
 
@@ -47,7 +54,7 @@ function storedContentId(msg: StoredContent): string {
47
54
 
48
55
  /** Convert zeitlich MessageContent to Google GenAI Part[] */
49
56
  export function messageContentToParts(
50
- content: string | MessageContent,
57
+ content: string | MessageContent
51
58
  ): Part[] {
52
59
  if (typeof content === "string") {
53
60
  return [{ text: content }];
@@ -65,7 +72,7 @@ export function messageContentToParts(
65
72
 
66
73
  /** Parse ToolMessageContent into a Record suitable for functionResponse */
67
74
  function parseToolResponse(
68
- content: ToolMessageContent,
75
+ content: ToolMessageContent
69
76
  ): Record<string, unknown> {
70
77
  if (typeof content === "string") {
71
78
  try {
@@ -86,7 +93,7 @@ function parseToolResponse(
86
93
  * appending typed Content messages.
87
94
  */
88
95
  export function createGoogleGenAIThreadManager(
89
- config: GoogleGenAIThreadManagerConfig,
96
+ config: GoogleGenAIThreadManagerConfig
90
97
  ): GoogleGenAIThreadManager {
91
98
  const baseConfig: ThreadManagerConfig<StoredContent> = {
92
99
  redis: config.redis,
@@ -98,34 +105,38 @@ export function createGoogleGenAIThreadManager(
98
105
  const base = createThreadManager(baseConfig);
99
106
 
100
107
  const helpers = {
101
- createUserContent(content: string | MessageContent): StoredContent {
108
+ createUserContent(
109
+ id: string,
110
+ content: string | MessageContent
111
+ ): StoredContent {
102
112
  return {
103
- id: crypto.randomUUID(),
113
+ id,
104
114
  content: { role: "user", parts: messageContentToParts(content) },
105
115
  };
106
116
  },
107
117
 
108
- createSystemContent(content: string): StoredContent {
118
+ createSystemContent(id: string, content: string): StoredContent {
109
119
  return {
110
- id: crypto.randomUUID(),
120
+ id,
111
121
  content: { role: "system", parts: [{ text: content }] },
112
122
  };
113
123
  },
114
124
 
115
- createModelContent(parts: Part[]): StoredContent {
125
+ createModelContent(id: string, parts: Part[]): StoredContent {
116
126
  return {
117
- id: crypto.randomUUID(),
127
+ id,
118
128
  content: { role: "model", parts },
119
129
  };
120
130
  },
121
131
 
122
132
  createToolResponseContent(
133
+ id: string,
123
134
  toolCallId: string,
124
135
  toolName: string,
125
- content: ToolMessageContent,
136
+ content: ToolMessageContent
126
137
  ): StoredContent {
127
138
  return {
128
- id: crypto.randomUUID(),
139
+ id,
129
140
  content: {
130
141
  role: "user",
131
142
  parts: [
@@ -141,26 +152,30 @@ export function createGoogleGenAIThreadManager(
141
152
  };
142
153
  },
143
154
 
144
- async appendUserMessage(content: string | MessageContent): Promise<void> {
145
- await base.append([helpers.createUserContent(content)]);
155
+ async appendUserMessage(
156
+ id: string,
157
+ content: string | MessageContent
158
+ ): Promise<void> {
159
+ await base.append([helpers.createUserContent(id, content)]);
146
160
  },
147
161
 
148
- async appendSystemMessage(content: string): Promise<void> {
162
+ async appendSystemMessage(id: string, content: string): Promise<void> {
149
163
  await base.initialize();
150
- await base.append([helpers.createSystemContent(content)]);
164
+ await base.append([helpers.createSystemContent(id, content)]);
151
165
  },
152
166
 
153
- async appendModelContent(parts: Part[]): Promise<void> {
154
- await base.append([helpers.createModelContent(parts)]);
167
+ async appendModelContent(id: string, parts: Part[]): Promise<void> {
168
+ await base.append([helpers.createModelContent(id, parts)]);
155
169
  },
156
170
 
157
171
  async appendToolResult(
172
+ id: string,
158
173
  toolCallId: string,
159
174
  toolName: string,
160
- content: ToolMessageContent,
175
+ content: ToolMessageContent
161
176
  ): Promise<void> {
162
177
  await base.append([
163
- helpers.createToolResponseContent(toolCallId, toolName, content),
178
+ helpers.createToolResponseContent(id, toolCallId, toolName, content),
164
179
  ]);
165
180
  },
166
181
  };
@@ -74,24 +74,26 @@ export function createLangChainAdapter(
74
74
 
75
75
  async appendHumanMessage(
76
76
  threadId: string,
77
+ id: string,
77
78
  content: string | MessageContent
78
79
  ): Promise<void> {
79
80
  const thread = createLangChainThreadManager({ redis, threadId });
80
- await thread.appendHumanMessage(content);
81
+ await thread.appendHumanMessage(id, content);
81
82
  },
82
83
 
83
84
  async appendSystemMessage(
84
85
  threadId: string,
86
+ id: string,
85
87
  content: string
86
88
  ): Promise<void> {
87
89
  const thread = createLangChainThreadManager({ redis, threadId });
88
- await thread.appendSystemMessage(content);
90
+ await thread.appendSystemMessage(id, content);
89
91
  },
90
92
 
91
- async appendToolResult(cfg: ToolResultConfig): Promise<void> {
93
+ async appendToolResult(id: string, cfg: ToolResultConfig): Promise<void> {
92
94
  const { threadId, toolCallId, content } = cfg;
93
95
  const thread = createLangChainThreadManager({ redis, threadId });
94
- await thread.appendToolMessage(content, toolCallId);
96
+ await thread.appendToolMessage(id, content, toolCallId);
95
97
  },
96
98
 
97
99
  async forkThread(
@@ -9,7 +9,6 @@ import {
9
9
  SystemMessage,
10
10
  ToolMessage,
11
11
  } from "@langchain/core/messages";
12
- import { v4 as uuidv4 } from "uuid";
13
12
  import {
14
13
  createThreadManager,
15
14
  type BaseThreadManager,
@@ -30,31 +29,44 @@ export interface LangChainThreadManagerConfig {
30
29
 
31
30
  /** Thread manager with LangChain StoredMessage convenience helpers */
32
31
  export interface LangChainThreadManager extends BaseThreadManager<StoredMessage> {
33
- createHumanMessage(content: string | MessageContent): StoredMessage;
34
- createSystemMessage(content: string): StoredMessage;
32
+ createHumanMessage(
33
+ id: string,
34
+ content: string | MessageContent
35
+ ): StoredMessage;
36
+ createSystemMessage(id: string, content: string): StoredMessage;
35
37
  createAIMessage(
38
+ id: string,
36
39
  content: string | MessageContent,
37
40
  kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
38
41
  ): StoredMessage;
39
42
  createToolMessage(
43
+ id: string,
40
44
  content: LangChainToolMessageContent,
41
45
  toolCallId: string
42
46
  ): StoredMessage;
43
- appendHumanMessage(content: string | MessageContent): Promise<void>;
44
- appendSystemMessage(content: string): Promise<void>;
47
+ appendHumanMessage(
48
+ id: string,
49
+ content: string | MessageContent
50
+ ): Promise<void>;
51
+ appendSystemMessage(id: string, content: string): Promise<void>;
45
52
  appendToolMessage(
53
+ id: string,
46
54
  content: LangChainToolMessageContent,
47
55
  toolCallId: string
48
56
  ): Promise<void>;
49
- appendAIMessage(content: string | MessageContent): Promise<void>;
57
+ appendAIMessage(id: string, content: string | MessageContent): Promise<void>;
50
58
  }
51
59
 
52
60
  function storedMessageId(msg: StoredMessage): string {
53
- if (msg.type === "tool") {
54
- return msg.data.tool_call_id ?? "";
61
+ if (msg.type === "tool" && msg.data.tool_call_id) {
62
+ return msg.data.tool_call_id;
63
+ }
64
+
65
+ if (msg.data.id) {
66
+ return msg.data.id;
55
67
  }
56
68
 
57
- return msg.data.id ?? "";
69
+ throw new Error("No id found for message");
58
70
  }
59
71
 
60
72
  /**
@@ -75,26 +87,30 @@ export function createLangChainThreadManager(
75
87
  const base = createThreadManager(baseConfig);
76
88
 
77
89
  const helpers = {
78
- createHumanMessage(content: string | MessageContent): StoredMessage {
90
+ createHumanMessage(
91
+ id: string,
92
+ content: string | MessageContent
93
+ ): StoredMessage {
79
94
  return new HumanMessage({
80
- id: uuidv4(),
95
+ id,
81
96
  content: content as string,
82
97
  }).toDict();
83
98
  },
84
99
 
85
- createSystemMessage(content: string): StoredMessage {
100
+ createSystemMessage(id: string, content: string): StoredMessage {
86
101
  return new SystemMessage({
87
- id: uuidv4(),
102
+ id,
88
103
  content: content as string,
89
104
  }).toDict();
90
105
  },
91
106
 
92
107
  createAIMessage(
108
+ id: string,
93
109
  content: string,
94
110
  kwargs?: { header?: string; options?: string[]; multiSelect?: boolean }
95
111
  ): StoredMessage {
96
112
  return new AIMessage({
97
- id: uuidv4(),
113
+ id,
98
114
  content,
99
115
  additional_kwargs: kwargs
100
116
  ? {
@@ -107,36 +123,44 @@ export function createLangChainThreadManager(
107
123
  },
108
124
 
109
125
  createToolMessage(
126
+ id: string,
110
127
  content: LangChainToolMessageContent,
111
128
  toolCallId: string
112
129
  ): StoredMessage {
113
130
  return new ToolMessage({
114
- id: uuidv4(),
131
+ id,
115
132
  content: content as MessageContent,
116
133
  tool_call_id: toolCallId,
117
134
  }).toDict();
118
135
  },
119
136
 
120
- async appendHumanMessage(content: string | MessageContent): Promise<void> {
121
- const message = helpers.createHumanMessage(content);
137
+ async appendHumanMessage(
138
+ id: string,
139
+ content: string | MessageContent
140
+ ): Promise<void> {
141
+ const message = helpers.createHumanMessage(id, content);
122
142
  await base.append([message]);
123
143
  },
124
144
 
125
145
  async appendToolMessage(
146
+ id: string,
126
147
  content: LangChainToolMessageContent,
127
148
  toolCallId: string
128
149
  ): Promise<void> {
129
- const message = helpers.createToolMessage(content, toolCallId);
150
+ const message = helpers.createToolMessage(id, content, toolCallId);
130
151
  await base.append([message]);
131
152
  },
132
153
 
133
- async appendAIMessage(content: string | MessageContent): Promise<void> {
134
- const message = helpers.createAIMessage(content as string);
154
+ async appendAIMessage(
155
+ id: string,
156
+ content: string | MessageContent
157
+ ): Promise<void> {
158
+ const message = helpers.createAIMessage(id, content as string);
135
159
  await base.append([message]);
136
160
  },
137
161
 
138
- async appendSystemMessage(content: string): Promise<void> {
139
- const message = helpers.createSystemMessage(content);
162
+ async appendSystemMessage(id: string, content: string): Promise<void> {
163
+ const message = helpers.createSystemMessage(id, content);
140
164
  await base.initialize();
141
165
  await base.append([message]);
142
166
  },
@@ -15,6 +15,7 @@ import type { ParsedToolCallUnion, ToolMap } from "../tool-router/types";
15
15
  import { getShortId } from "../thread/id";
16
16
  import { buildSubagentRegistration } from "../subagent/register";
17
17
  import { buildSkillRegistration } from "../skills/register";
18
+ import { uuid4 } from "@temporalio/workflow";
18
19
 
19
20
  /**
20
21
  * Creates an agent session that manages the agent loop: LLM invocation,
@@ -134,7 +135,7 @@ export const createSession = async <T extends ToolMap, M = unknown>({
134
135
  threadId,
135
136
  });
136
137
  }
137
- await appendHumanMessage(threadId, message);
138
+ await appendHumanMessage(threadId, uuid4(), message);
138
139
  if (hooks.onPostHumanMessageAppend) {
139
140
  await hooks.onPostHumanMessageAppend({
140
141
  message,
@@ -176,12 +177,12 @@ export const createSession = async <T extends ToolMap, M = unknown>({
176
177
  nonRetryable: true,
177
178
  });
178
179
  }
179
- await appendSystemMessage(threadId, systemPrompt);
180
+ await appendSystemMessage(threadId, uuid4(), systemPrompt);
180
181
  } else {
181
182
  await initializeThread(threadId);
182
183
  }
183
184
  }
184
- await appendHumanMessage(threadId, await buildContextMessage());
185
+ await appendHumanMessage(threadId, uuid4(), await buildContextMessage());
185
186
 
186
187
  let exitReason: SessionExitReason = "completed";
187
188
 
@@ -222,7 +223,7 @@ export const createSession = async <T extends ToolMap, M = unknown>({
222
223
  try {
223
224
  parsedToolCalls.push(toolRouter.parseToolCall(tc));
224
225
  } catch (error) {
225
- await appendToolResult({
226
+ await appendToolResult(uuid4(), {
226
227
  threadId,
227
228
  toolCallId: tc.id ?? "",
228
229
  toolName: tc.name,
@@ -27,12 +27,17 @@ export interface ThreadOps {
27
27
  /** Append a human message to the thread */
28
28
  appendHumanMessage(
29
29
  threadId: string,
30
+ id: string,
30
31
  content: string | MessageContent
31
32
  ): Promise<void>;
32
33
  /** Append a tool result to the thread */
33
- appendToolResult(config: ToolResultConfig): Promise<void>;
34
+ appendToolResult(id: string, config: ToolResultConfig): Promise<void>;
34
35
  /** Append a system message to the thread */
35
- appendSystemMessage(threadId: string, content: string): Promise<void>;
36
+ appendSystemMessage(
37
+ threadId: string,
38
+ id: string,
39
+ content: string
40
+ ): Promise<void>;
36
41
  /** Copy all messages from sourceThreadId into a new thread at targetThreadId */
37
42
  forkThread(sourceThreadId: string, targetThreadId: string): Promise<void>;
38
43
  }
@@ -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");