zeitlich 0.2.2 → 0.2.4

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/README.md +34 -31
  2. package/dist/index.cjs +330 -399
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +24 -43
  5. package/dist/index.d.ts +24 -43
  6. package/dist/index.js +301 -373
  7. package/dist/index.js.map +1 -1
  8. package/dist/{workflow-BQf5EfNN.d.cts → workflow-PjeURKw4.d.cts} +265 -258
  9. package/dist/{workflow-BQf5EfNN.d.ts → workflow-PjeURKw4.d.ts} +265 -258
  10. package/dist/workflow.cjs +223 -281
  11. package/dist/workflow.cjs.map +1 -1
  12. package/dist/workflow.d.cts +2 -3
  13. package/dist/workflow.d.ts +2 -3
  14. package/dist/workflow.js +198 -258
  15. package/dist/workflow.js.map +1 -1
  16. package/package.json +3 -2
  17. package/src/activities.ts +0 -32
  18. package/src/index.ts +14 -11
  19. package/src/lib/model-invoker.ts +7 -1
  20. package/src/lib/session.ts +54 -109
  21. package/src/lib/thread-manager.ts +45 -37
  22. package/src/lib/tool-router.ts +148 -108
  23. package/src/lib/types.ts +35 -26
  24. package/src/tools/ask-user-question/handler.ts +5 -5
  25. package/src/tools/ask-user-question/tool.ts +3 -2
  26. package/src/tools/bash/bash.test.ts +12 -12
  27. package/src/tools/bash/handler.ts +5 -5
  28. package/src/tools/bash/tool.ts +3 -2
  29. package/src/tools/edit/handler.ts +78 -123
  30. package/src/tools/edit/tool.ts +3 -2
  31. package/src/tools/glob/handler.ts +17 -48
  32. package/src/tools/glob/tool.ts +3 -2
  33. package/src/tools/grep/tool.ts +3 -2
  34. package/src/tools/{read → read-file}/tool.ts +3 -2
  35. package/src/tools/{task → subagent}/handler.ts +18 -31
  36. package/src/tools/{task → subagent}/tool.ts +13 -20
  37. package/src/tools/task-create/handler.ts +5 -11
  38. package/src/tools/task-create/tool.ts +3 -2
  39. package/src/tools/task-get/handler.ts +5 -10
  40. package/src/tools/task-get/tool.ts +3 -2
  41. package/src/tools/task-list/handler.ts +5 -10
  42. package/src/tools/task-list/tool.ts +3 -2
  43. package/src/tools/task-update/handler.ts +5 -12
  44. package/src/tools/task-update/tool.ts +3 -2
  45. package/src/tools/{write → write-file}/tool.ts +5 -6
  46. package/src/workflow.ts +24 -21
package/dist/index.js CHANGED
@@ -1,52 +1,52 @@
1
1
  import { defineQuery, proxyActivities, setHandler, uuid4, workflowInfo, executeChild } from '@temporalio/workflow';
2
- import z5, { z } from 'zod';
2
+ import z3, { z } from 'zod';
3
3
  import { SimplePlugin } from '@temporalio/plugin';
4
- import { SystemMessage, ToolMessage, AIMessage, HumanMessage, mapStoredMessageToChatMessage, mapStoredMessagesToChatMessages } from '@langchain/core/messages';
4
+ import { mapStoredMessagesToChatMessages, AIMessage, ToolMessage, HumanMessage } from '@langchain/core/messages';
5
5
  import crypto from 'crypto';
6
6
  import { Context } from '@temporalio/activity';
7
7
  import { Bash } from 'just-bash';
8
8
 
9
9
  // src/lib/session.ts
10
- var TASK_TOOL = "Task";
11
- function buildTaskDescription(subagents) {
10
+ var SUBAGENT_TOOL = "Subagent";
11
+ function buildSubagentDescription(subagents) {
12
12
  const subagentList = subagents.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
13
- return `Launch a new agent to handle complex, multi-step tasks autonomously.
13
+ return `Launch a new agent to handle complex tasks autonomously.
14
14
 
15
- The ${TASK_TOOL} tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
15
+ The ${SUBAGENT_TOOL} tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
16
16
 
17
17
  Available agent types:
18
18
 
19
19
  ${subagentList}
20
20
 
21
- When using the ${TASK_TOOL} tool, you must specify a subagent parameter to select which agent type to use.
21
+ When using the ${SUBAGENT_TOOL} tool, you must specify a subagent parameter to select which agent type to use.
22
22
 
23
23
  Usage notes:
24
24
 
25
25
  - Always include a short description (3-5 words) summarizing what the agent will do
26
26
  - Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
27
- - When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
27
+ - When the agent is done, it will return a single message back to you.
28
28
  - Each invocation starts fresh - provide a detailed task description with all necessary context.
29
29
  - Provide clear, detailed prompts so the agent can work autonomously and return exactly the information you need.
30
30
  - The agent's outputs should generally be trusted
31
31
  - Clearly tell the agent what type of work you expect since it is not aware of the user's intent
32
32
  - If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.`;
33
33
  }
34
- function createTaskTool(subagents) {
34
+ function createSubagentTool(subagents) {
35
35
  if (subagents.length === 0) {
36
36
  throw new Error("createTaskTool requires at least one subagent");
37
37
  }
38
38
  const names = subagents.map((s) => s.name);
39
39
  return {
40
- name: TASK_TOOL,
41
- description: buildTaskDescription(subagents),
42
- schema: z5.object({
43
- subagent: z5.enum(names).describe("The type of subagent to launch"),
44
- description: z5.string().describe("A short (3-5 word) description of the task"),
45
- prompt: z5.string().describe("The task for the agent to perform")
40
+ name: SUBAGENT_TOOL,
41
+ description: buildSubagentDescription(subagents),
42
+ schema: z3.object({
43
+ subagent: z3.enum(names).describe("The type of subagent to launch"),
44
+ description: z3.string().describe("A short (3-5 word) description of the task"),
45
+ prompt: z3.string().describe("The task for the agent to perform")
46
46
  })
47
47
  };
48
48
  }
49
- function createTaskHandler(subagents) {
49
+ function createSubagentHandler(subagents) {
50
50
  const { workflowId: parentWorkflowId, taskQueue: parentTaskQueue } = workflowInfo();
51
51
  return async (args) => {
52
52
  const config = subagents.find((s) => s.name === args.subagent);
@@ -65,128 +65,32 @@ function createTaskHandler(subagents) {
65
65
  args: [input],
66
66
  taskQueue: config.taskQueue ?? parentTaskQueue
67
67
  };
68
- const childResult = typeof config.workflow === "string" ? await executeChild(config.workflow, childOpts) : await executeChild(config.workflow, childOpts);
69
- const validated = config.resultSchema ? config.resultSchema.parse(childResult) : childResult;
70
- const toolResponse = typeof validated === "string" ? validated : JSON.stringify(validated, null, 2);
68
+ const { toolResponse, data } = typeof config.workflow === "string" ? await executeChild(config.workflow, childOpts) : await executeChild(config.workflow, childOpts);
69
+ const validated = config.resultSchema ? config.resultSchema.parse(data) : null;
71
70
  return {
72
71
  toolResponse,
73
- data: {
74
- result: validated,
75
- childWorkflowId
76
- }
72
+ data: validated
77
73
  };
78
74
  };
79
75
  }
80
- var createBashToolDescription = ({
81
- fileTree
82
- }) => `Execute shell commands in a bash environment.
83
-
84
- Use this tool to:
85
- - Run shell commands (ls, cat, grep, find, etc.)
86
- - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
87
- - Inspect files and directories
88
-
89
- Current file tree:
90
- ${fileTree}`;
91
- var bashTool = {
92
- name: "Bash",
93
- description: `Execute shell commands in a sandboxed bash environment.
94
-
95
- Use this tool to:
96
- - Run shell commands (ls, cat, grep, find, etc.)
97
- - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
98
- - Inspect files and directories
99
- `,
100
- schema: z5.object({
101
- command: z5.string().describe(
102
- "The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
103
- )
104
- }),
105
- strict: true
106
- };
107
- var taskCreateTool = {
108
- name: "TaskCreate",
109
- description: `Use this tool to create a structured task list for the control test. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
110
- It also helps the user understand the progress of the task and overall progress of their requests.
111
-
112
- ## When to Use This Tool
113
-
114
- Use this tool proactively in these scenarios:
115
-
116
- - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
117
- - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
118
- - User explicitly requests todo list - When the user directly asks you to use the todo list
119
- - User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
120
- - After receiving new instructions - Immediately capture user requirements as tasks
121
- - When you start working on a task - Mark it as in_progress BEFORE beginning work
122
- - After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
123
-
124
- ## When NOT to Use This Tool
125
-
126
- Skip using this tool when:
127
- - There is only a single, straightforward task
128
- - The task is trivial and tracking it provides no organizational benefit
129
- - The task can be completed in less than 3 trivial steps
130
- - The task is purely conversational or informational
131
-
132
- NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.
133
-
134
- ## Task Fields
135
-
136
- - **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
137
- - **description**: Detailed description of what needs to be done, including context and acceptance criteria
138
- - **activeForm**: Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.
139
-
140
- **IMPORTANT**: Always provide activeForm when creating tasks. The subject should be imperative ("Run tests") while activeForm should be present continuous ("Running tests"). All tasks are created with status \`pending\`.
141
-
142
- ## Tips
143
-
144
- - Create tasks with clear, specific subjects that describe the outcome
145
- - Include enough detail in the description for another agent to understand and complete the task
146
- - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
147
- - Check TaskList first to avoid creating duplicate tasks`,
148
- schema: z5.object({
149
- subject: z5.string().describe(
150
- 'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
151
- ),
152
- description: z5.string().describe(
153
- "Detailed description of what needs to be done, including context and acceptance criteria"
154
- ),
155
- activeForm: z5.string().describe(
156
- 'Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.'
157
- ),
158
- metadata: z5.record(z5.string(), z5.string()).describe("Arbitrary key-value pairs for tracking")
159
- })
160
- };
161
76
 
162
77
  // src/lib/tool-router.ts
163
- var buildIntoolDefinitions = {
164
- [bashTool.name]: bashTool,
165
- [taskCreateTool.name]: taskCreateTool
166
- };
167
78
  function createToolRouter(options) {
168
- const { appendToolResult } = proxyActivities({
169
- startToCloseTimeout: "2m",
170
- retry: {
171
- maximumAttempts: 3,
172
- initialInterval: "5s",
173
- maximumInterval: "15m",
174
- backoffCoefficient: 4
175
- }
176
- });
79
+ const { appendToolResult } = options;
177
80
  const toolMap = /* @__PURE__ */ new Map();
178
81
  for (const [_key, tool] of Object.entries(options.tools)) {
179
82
  toolMap.set(tool.name, tool);
180
83
  }
84
+ const isEnabled = (tool) => tool.enabled !== false;
181
85
  if (options.subagents) {
182
86
  const subagentHooksMap = /* @__PURE__ */ new Map();
183
87
  for (const s of options.subagents) {
184
88
  if (s.hooks) subagentHooksMap.set(s.name, s.hooks);
185
89
  }
186
90
  const resolveSubagentName = (args) => args.subagent;
187
- toolMap.set("Task", {
188
- ...createTaskTool(options.subagents),
189
- handler: createTaskHandler(options.subagents),
91
+ toolMap.set("Subagent", {
92
+ ...createSubagentTool(options.subagents),
93
+ handler: createSubagentHandler(options.subagents),
190
94
  ...subagentHooksMap.size > 0 && {
191
95
  hooks: {
192
96
  onPreToolUse: async (ctx) => {
@@ -205,24 +109,6 @@ function createToolRouter(options) {
205
109
  }
206
110
  });
207
111
  }
208
- if (options.buildInTools) {
209
- for (const [key, value] of Object.entries(options.buildInTools)) {
210
- if (key === bashTool.name) {
211
- toolMap.set(key, {
212
- ...buildIntoolDefinitions[key],
213
- description: createBashToolDescription({
214
- fileTree: options.fileTree
215
- }),
216
- handler: value
217
- });
218
- } else {
219
- toolMap.set(key, {
220
- ...buildIntoolDefinitions[key],
221
- handler: value
222
- });
223
- }
224
- }
225
- }
226
112
  async function processToolCall(toolCall, turn, handlerContext) {
227
113
  const startTime = Date.now();
228
114
  const tool = toolMap.get(toolCall.name);
@@ -238,6 +124,7 @@ function createToolRouter(options) {
238
124
  await appendToolResult({
239
125
  threadId: options.threadId,
240
126
  toolCallId: toolCall.id,
127
+ toolName: toolCall.name,
241
128
  content: JSON.stringify({
242
129
  skipped: true,
243
130
  reason: "Skipped by PreToolUse hook"
@@ -259,6 +146,7 @@ function createToolRouter(options) {
259
146
  await appendToolResult({
260
147
  threadId: options.threadId,
261
148
  toolCallId: toolCall.id,
149
+ toolName: toolCall.name,
262
150
  content: JSON.stringify({
263
151
  skipped: true,
264
152
  reason: "Skipped by tool PreToolUse hook"
@@ -272,14 +160,22 @@ function createToolRouter(options) {
272
160
  }
273
161
  let result;
274
162
  let content;
163
+ let resultAppended = false;
275
164
  try {
276
165
  if (tool) {
166
+ const enrichedContext = {
167
+ ...handlerContext ?? {},
168
+ threadId: options.threadId,
169
+ toolCallId: toolCall.id,
170
+ toolName: toolCall.name
171
+ };
277
172
  const response = await tool.handler(
278
173
  effectiveArgs,
279
- handlerContext ?? {}
174
+ enrichedContext
280
175
  );
281
176
  result = response.data;
282
177
  content = response.toolResponse;
178
+ resultAppended = response.resultAppended === true;
283
179
  } else {
284
180
  result = { error: `Unknown tool: ${toolCall.name}` };
285
181
  content = JSON.stringify(result, null, 2);
@@ -325,11 +221,14 @@ function createToolRouter(options) {
325
221
  throw error;
326
222
  }
327
223
  }
328
- await appendToolResult({
329
- threadId: options.threadId,
330
- toolCallId: toolCall.id,
331
- content
332
- });
224
+ if (!resultAppended) {
225
+ await appendToolResult({
226
+ threadId: options.threadId,
227
+ toolCallId: toolCall.id,
228
+ toolName: toolCall.name,
229
+ content
230
+ });
231
+ }
333
232
  const toolResult = {
334
233
  toolCallId: toolCall.id,
335
234
  name: toolCall.name,
@@ -359,11 +258,11 @@ function createToolRouter(options) {
359
258
  return {
360
259
  // --- Methods from registry ---
361
260
  hasTools() {
362
- return toolMap.size > 0;
261
+ return Array.from(toolMap.values()).some(isEnabled);
363
262
  },
364
263
  parseToolCall(toolCall) {
365
264
  const tool = toolMap.get(toolCall.name);
366
- if (!tool) {
265
+ if (!tool || !isEnabled(tool)) {
367
266
  throw new Error(`Tool ${toolCall.name} not found`);
368
267
  }
369
268
  const parsedArgs = tool.schema.parse(toolCall.args);
@@ -374,13 +273,14 @@ function createToolRouter(options) {
374
273
  };
375
274
  },
376
275
  hasTool(name) {
377
- return toolMap.has(name);
276
+ const tool = toolMap.get(name);
277
+ return tool !== void 0 && isEnabled(tool);
378
278
  },
379
279
  getToolNames() {
380
- return Array.from(toolMap.keys());
280
+ return Array.from(toolMap.entries()).filter(([, tool]) => isEnabled(tool)).map(([name]) => name);
381
281
  },
382
282
  getToolDefinitions() {
383
- return Array.from(toolMap).map(([name, tool]) => ({
283
+ return Array.from(toolMap).filter(([, tool]) => isEnabled(tool)).map(([name, tool]) => ({
384
284
  name,
385
285
  description: tool.description,
386
286
  schema: tool.schema,
@@ -419,19 +319,28 @@ function createToolRouter(options) {
419
319
  }
420
320
  const handlerContext = context?.handlerContext ?? {};
421
321
  const processOne = async (toolCall) => {
322
+ const enrichedContext = {
323
+ ...handlerContext ?? {},
324
+ threadId: options.threadId,
325
+ toolCallId: toolCall.id,
326
+ toolName: toolCall.name
327
+ };
422
328
  const response = await handler(
423
329
  toolCall.args,
424
- handlerContext
330
+ enrichedContext
425
331
  );
426
- await appendToolResult({
427
- threadId: options.threadId,
428
- toolCallId: toolCall.id,
429
- content: response.toolResponse
430
- });
332
+ if (!response.resultAppended) {
333
+ await appendToolResult({
334
+ threadId: options.threadId,
335
+ toolCallId: toolCall.id,
336
+ toolName: toolCall.name,
337
+ content: response.toolResponse
338
+ });
339
+ }
431
340
  return {
432
341
  toolCallId: toolCall.id,
433
342
  name: toolCall.name,
434
- data: response.data ?? null
343
+ data: response.data
435
344
  };
436
345
  };
437
346
  if (options.parallel) {
@@ -457,6 +366,21 @@ function createToolRouter(options) {
457
366
  }
458
367
  };
459
368
  }
369
+ function withAutoAppend(threadHandler, handler) {
370
+ return async (args, context) => {
371
+ const response = await handler(args, context);
372
+ const threadId = context.threadId;
373
+ const toolCallId = context.toolCallId;
374
+ const toolName = context.toolName;
375
+ await threadHandler({
376
+ threadId,
377
+ toolCallId,
378
+ toolName,
379
+ content: response.toolResponse
380
+ });
381
+ return { toolResponse: "", data: response.data, resultAppended: true };
382
+ };
383
+ }
460
384
  function defineTool(tool) {
461
385
  return tool;
462
386
  }
@@ -468,51 +392,24 @@ function hasNoOtherToolCalls(toolCalls, excludeName) {
468
392
  }
469
393
 
470
394
  // src/lib/session.ts
471
- async function resolvePrompt(prompt) {
472
- if (typeof prompt === "function") {
473
- return prompt();
474
- }
475
- return prompt;
476
- }
477
395
  var createSession = async ({
478
396
  threadId,
479
397
  agentName,
480
398
  maxTurns = 50,
481
399
  metadata = {},
482
400
  runAgent,
483
- baseSystemPrompt,
484
- instructionsPrompt,
401
+ threadOps,
485
402
  buildContextMessage,
486
- buildFileTree = async () => "",
487
403
  subagents,
488
404
  tools = {},
489
405
  processToolsInParallel = true,
490
- buildInTools = {},
491
406
  hooks = {}
492
407
  }) => {
493
- const {
494
- initializeThread,
495
- appendHumanMessage,
496
- parseToolCalls,
497
- appendToolResult,
498
- appendSystemMessage
499
- } = proxyActivities({
500
- startToCloseTimeout: "30m",
501
- retry: {
502
- maximumAttempts: 6,
503
- initialInterval: "5s",
504
- maximumInterval: "15m",
505
- backoffCoefficient: 4
506
- },
507
- heartbeatTimeout: "5m"
508
- });
509
- const fileTree = await buildFileTree();
510
408
  const toolRouter = createToolRouter({
511
409
  tools,
410
+ appendToolResult: threadOps.appendToolResult,
512
411
  threadId,
513
412
  hooks,
514
- buildInTools,
515
- fileTree,
516
413
  subagents,
517
414
  parallel: processToolsInParallel
518
415
  });
@@ -537,83 +434,41 @@ var createSession = async ({
537
434
  });
538
435
  }
539
436
  stateManager.setTools(toolRouter.getToolDefinitions());
540
- await initializeThread(threadId);
541
- await appendSystemMessage(
542
- threadId,
543
- [
544
- await resolvePrompt(baseSystemPrompt),
545
- await resolvePrompt(instructionsPrompt)
546
- ].join("\n")
547
- );
548
- await appendHumanMessage(threadId, await buildContextMessage());
437
+ await threadOps.initializeThread(threadId);
438
+ await threadOps.appendHumanMessage(threadId, await buildContextMessage());
549
439
  let exitReason = "completed";
550
440
  try {
551
441
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
552
442
  stateManager.incrementTurns();
553
443
  const currentTurn = stateManager.getTurns();
554
- const { message, stopReason } = await runAgent({
444
+ const { message, rawToolCalls } = await runAgent({
555
445
  threadId,
556
446
  agentName,
557
447
  metadata
558
448
  });
559
- if (stopReason === "end_turn") {
560
- stateManager.complete();
561
- exitReason = "completed";
562
- return message;
563
- }
564
- if (!toolRouter.hasTools()) {
449
+ if (!toolRouter.hasTools() || rawToolCalls.length === 0) {
565
450
  stateManager.complete();
566
451
  exitReason = "completed";
567
452
  return message;
568
453
  }
569
- const rawToolCalls = await parseToolCalls(message);
570
454
  const parsedToolCalls = [];
571
- for (const tc of rawToolCalls.filter(
572
- (tc2) => tc2.name !== "Task"
573
- )) {
455
+ for (const tc of rawToolCalls) {
574
456
  try {
575
457
  parsedToolCalls.push(toolRouter.parseToolCall(tc));
576
458
  } catch (error) {
577
- await appendToolResult({
459
+ await threadOps.appendToolResult({
578
460
  threadId,
579
461
  toolCallId: tc.id ?? "",
462
+ toolName: tc.name,
580
463
  content: JSON.stringify({
581
464
  error: `Invalid tool call for "${tc.name}": ${error instanceof Error ? error.message : String(error)}`
582
465
  })
583
466
  });
584
467
  }
585
468
  }
586
- const taskToolCalls = [];
587
- if (subagents && subagents.length > 0) {
588
- for (const tc of rawToolCalls.filter(
589
- (tc2) => tc2.name === "Task"
590
- )) {
591
- try {
592
- const parsedArgs = createTaskTool(subagents).schema.parse(
593
- tc.args
594
- );
595
- taskToolCalls.push({
596
- id: tc.id ?? "",
597
- name: tc.name,
598
- args: parsedArgs
599
- });
600
- } catch (error) {
601
- await appendToolResult({
602
- threadId,
603
- toolCallId: tc.id ?? "",
604
- content: JSON.stringify({
605
- error: `Invalid tool call for "Task": ${error instanceof Error ? error.message : String(error)}`
606
- })
607
- });
608
- }
609
- }
610
- }
611
- await toolRouter.processToolCalls(
612
- [...parsedToolCalls, ...taskToolCalls],
613
- {
614
- turn: currentTurn
615
- }
616
- );
469
+ await toolRouter.processToolCalls(parsedToolCalls, {
470
+ turn: currentTurn
471
+ });
617
472
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
618
473
  exitReason = "waiting_for_input";
619
474
  break;
@@ -632,6 +487,25 @@ var createSession = async ({
632
487
  }
633
488
  };
634
489
  };
490
+ function proxyDefaultThreadOps(options) {
491
+ const activities = proxyActivities(
492
+ options ?? {
493
+ startToCloseTimeout: "30m",
494
+ retry: {
495
+ maximumAttempts: 6,
496
+ initialInterval: "5s",
497
+ maximumInterval: "15m",
498
+ backoffCoefficient: 4
499
+ },
500
+ heartbeatTimeout: "5m"
501
+ }
502
+ );
503
+ return {
504
+ initializeThread: activities.initializeThread,
505
+ appendHumanMessage: activities.appendHumanMessage,
506
+ appendToolResult: activities.appendToolResult
507
+ };
508
+ }
635
509
 
636
510
  // src/lib/types.ts
637
511
  function isTerminalStatus(status) {
@@ -768,18 +642,18 @@ Usage notes:
768
642
  * Use multiSelect: true to allow multiple answers to be selected for a question
769
643
  * If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
770
644
  `,
771
- schema: z5.object({
772
- questions: z5.array(
773
- z5.object({
774
- question: z5.string().describe("The full question text to display"),
775
- header: z5.string().describe("Short label for the question (max 12 characters)"),
776
- options: z5.array(
777
- z5.object({
778
- label: z5.string(),
779
- description: z5.string()
645
+ schema: z3.object({
646
+ questions: z3.array(
647
+ z3.object({
648
+ question: z3.string().describe("The full question text to display"),
649
+ header: z3.string().describe("Short label for the question (max 12 characters)"),
650
+ options: z3.array(
651
+ z3.object({
652
+ label: z3.string(),
653
+ description: z3.string()
780
654
  })
781
655
  ).min(0).max(4).describe("Array of 0-4 choices, each with label and description"),
782
- multiSelect: z5.boolean().describe("If true, users can select multiple options")
656
+ multiSelect: z3.boolean().describe("If true, users can select multiple options")
783
657
  })
784
658
  )
785
659
  }),
@@ -857,7 +731,7 @@ var writeTool = {
857
731
  description: `Create or overwrite a file with new content.
858
732
 
859
733
  Usage:
860
- - Provide the absolute virtual path to the file
734
+ - Provide the absolute path to the file
861
735
  - The file will be created if it doesn't exist
862
736
  - If the file exists, it will be completely overwritten
863
737
 
@@ -867,7 +741,7 @@ IMPORTANT:
867
741
  - Path must be absolute (e.g., "/docs/readme.md", not "docs/readme.md")
868
742
  `,
869
743
  schema: z.object({
870
- file_path: z.string().describe("The absolute virtual path to the file to write"),
744
+ file_path: z.string().describe("The absolute path to the file to write"),
871
745
  content: z.string().describe("The content to write to the file")
872
746
  }),
873
747
  strict: true
@@ -900,6 +774,60 @@ IMPORTANT:
900
774
  }),
901
775
  strict: true
902
776
  };
777
+ var taskCreateTool = {
778
+ name: "TaskCreate",
779
+ description: `Use this tool to create a structured task list for the control test. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
780
+ It also helps the user understand the progress of the task and overall progress of their requests.
781
+
782
+ ## When to Use This Tool
783
+
784
+ Use this tool proactively in these scenarios:
785
+
786
+ - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
787
+ - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
788
+ - User explicitly requests todo list - When the user directly asks you to use the todo list
789
+ - User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
790
+ - After receiving new instructions - Immediately capture user requirements as tasks
791
+ - When you start working on a task - Mark it as in_progress BEFORE beginning work
792
+ - After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
793
+
794
+ ## When NOT to Use This Tool
795
+
796
+ Skip using this tool when:
797
+ - There is only a single, straightforward task
798
+ - The task is trivial and tracking it provides no organizational benefit
799
+ - The task can be completed in less than 3 trivial steps
800
+ - The task is purely conversational or informational
801
+
802
+ NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.
803
+
804
+ ## Task Fields
805
+
806
+ - **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
807
+ - **description**: Detailed description of what needs to be done, including context and acceptance criteria
808
+ - **activeForm**: Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.
809
+
810
+ **IMPORTANT**: Always provide activeForm when creating tasks. The subject should be imperative ("Run tests") while activeForm should be present continuous ("Running tests"). All tasks are created with status \`pending\`.
811
+
812
+ ## Tips
813
+
814
+ - Create tasks with clear, specific subjects that describe the outcome
815
+ - Include enough detail in the description for another agent to understand and complete the task
816
+ - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
817
+ - Check TaskList first to avoid creating duplicate tasks`,
818
+ schema: z3.object({
819
+ subject: z3.string().describe(
820
+ 'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
821
+ ),
822
+ description: z3.string().describe(
823
+ "Detailed description of what needs to be done, including context and acceptance criteria"
824
+ ),
825
+ activeForm: z3.string().describe(
826
+ 'Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.'
827
+ ),
828
+ metadata: z3.record(z3.string(), z3.string()).describe("Arbitrary key-value pairs for tracking")
829
+ })
830
+ };
903
831
  function createTaskCreateHandler(stateManager) {
904
832
  return (args) => {
905
833
  const task = {
@@ -922,8 +850,8 @@ function createTaskCreateHandler(stateManager) {
922
850
  var taskGetTool = {
923
851
  name: "TaskGet",
924
852
  description: `Retrieve full task details including dependencies.`,
925
- schema: z5.object({
926
- taskId: z5.string().describe("The ID of the task to get")
853
+ schema: z3.object({
854
+ taskId: z3.string().describe("The ID of the task to get")
927
855
  })
928
856
  };
929
857
 
@@ -946,12 +874,12 @@ function createTaskGetHandler(stateManager) {
946
874
  var taskListTool = {
947
875
  name: "TaskList",
948
876
  description: `List all tasks with current state.`,
949
- schema: z5.object({})
877
+ schema: z3.object({})
950
878
  };
951
879
 
952
880
  // src/tools/task-list/handler.ts
953
881
  function createTaskListHandler(stateManager) {
954
- return (_args) => {
882
+ return () => {
955
883
  const taskList = stateManager.getTasks();
956
884
  return {
957
885
  toolResponse: JSON.stringify(taskList, null, 2),
@@ -962,11 +890,11 @@ function createTaskListHandler(stateManager) {
962
890
  var taskUpdateTool = {
963
891
  name: "TaskUpdate",
964
892
  description: `Update status, add blockers, modify details.`,
965
- schema: z5.object({
966
- taskId: z5.string().describe("The ID of the task to get"),
967
- status: z5.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
968
- addBlockedBy: z5.array(z5.string()).describe("The IDs of the tasks that are blocking this task"),
969
- addBlocks: z5.array(z5.string()).describe("The IDs of the tasks that this task is blocking")
893
+ schema: z3.object({
894
+ taskId: z3.string().describe("The ID of the task to get"),
895
+ status: z3.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
896
+ addBlockedBy: z3.array(z3.string()).describe("The IDs of the tasks that are blocking this task"),
897
+ addBlocks: z3.array(z3.string()).describe("The IDs of the tasks that this task is blocking")
970
898
  })
971
899
  };
972
900
 
@@ -1014,6 +942,33 @@ function createTaskUpdateHandler(stateManager) {
1014
942
  };
1015
943
  };
1016
944
  }
945
+ var createBashToolDescription = ({
946
+ fileTree
947
+ }) => `Execute shell commands in a bash environment.
948
+
949
+ Use this tool to:
950
+ - Run shell commands (ls, cat, grep, find, etc.)
951
+ - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
952
+ - Inspect files and directories
953
+
954
+ Current file tree:
955
+ ${fileTree}`;
956
+ var bashTool = {
957
+ name: "Bash",
958
+ description: `Execute shell commands in a sandboxed bash environment.
959
+
960
+ Use this tool to:
961
+ - Run shell commands (ls, cat, grep, find, etc.)
962
+ - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
963
+ - Inspect files and directories
964
+ `,
965
+ schema: z3.object({
966
+ command: z3.string().describe(
967
+ "The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
968
+ )
969
+ }),
970
+ strict: true
971
+ };
1017
972
 
1018
973
  // node_modules/uuid/dist/esm-node/stringify.js
1019
974
  var byteToHex = [];
@@ -1062,25 +1017,33 @@ function getThreadKey(threadId, key) {
1062
1017
  return `thread:${threadId}:${key}`;
1063
1018
  }
1064
1019
  function createThreadManager(config) {
1065
- const { redis, threadId, key = "messages" } = config;
1020
+ const {
1021
+ redis,
1022
+ threadId,
1023
+ key = "messages",
1024
+ serialize = (m) => JSON.stringify(m),
1025
+ deserialize = (raw) => JSON.parse(raw)
1026
+ } = config;
1066
1027
  const redisKey = getThreadKey(threadId, key);
1067
- return {
1028
+ const base = {
1068
1029
  async initialize() {
1069
1030
  await redis.del(redisKey);
1070
1031
  },
1071
1032
  async load() {
1072
1033
  const data = await redis.lrange(redisKey, 0, -1);
1073
- return data.map((item) => JSON.parse(item));
1034
+ return data.map(deserialize);
1074
1035
  },
1075
1036
  async append(messages) {
1076
1037
  if (messages.length > 0) {
1077
- await redis.rpush(redisKey, ...messages.map((m) => JSON.stringify(m)));
1038
+ await redis.rpush(redisKey, ...messages.map(serialize));
1078
1039
  await redis.expire(redisKey, THREAD_TTL_SECONDS);
1079
1040
  }
1080
1041
  },
1081
1042
  async delete() {
1082
1043
  await redis.del(redisKey);
1083
- },
1044
+ }
1045
+ };
1046
+ const helpers = {
1084
1047
  createHumanMessage(content) {
1085
1048
  return new HumanMessage({
1086
1049
  id: v4_default(),
@@ -1100,40 +1063,29 @@ function createThreadManager(config) {
1100
1063
  },
1101
1064
  createToolMessage(content, toolCallId) {
1102
1065
  return new ToolMessage({
1103
- // Cast needed due to langchain type compatibility
1104
1066
  content,
1105
1067
  tool_call_id: toolCallId
1106
1068
  }).toDict();
1107
1069
  },
1108
- createSystemMessage(content) {
1109
- return new SystemMessage({
1110
- content
1111
- }).toDict();
1112
- },
1113
- async appendSystemMessage(content) {
1114
- const message = this.createSystemMessage(content);
1115
- await this.append([message]);
1116
- },
1117
1070
  async appendHumanMessage(content) {
1118
- const message = this.createHumanMessage(content);
1119
- await this.append([message]);
1071
+ const message = helpers.createHumanMessage(content);
1072
+ await base.append([message]);
1120
1073
  },
1121
1074
  async appendToolMessage(content, toolCallId) {
1122
- const message = this.createToolMessage(content, toolCallId);
1123
- await this.append([message]);
1075
+ const message = helpers.createToolMessage(content, toolCallId);
1076
+ await base.append([message]);
1124
1077
  },
1125
1078
  async appendAIMessage(content) {
1126
- const message = this.createAIMessage(content);
1127
- await this.append([message]);
1079
+ const message = helpers.createAIMessage(content);
1080
+ await base.append([message]);
1128
1081
  }
1129
1082
  };
1083
+ return Object.assign(base, helpers);
1130
1084
  }
1085
+
1086
+ // src/activities.ts
1131
1087
  function createSharedActivities(redis) {
1132
1088
  return {
1133
- async appendSystemMessage(threadId, content) {
1134
- const thread = createThreadManager({ redis, threadId });
1135
- await thread.appendSystemMessage(content);
1136
- },
1137
1089
  async appendToolResult(config) {
1138
1090
  const { threadId, toolCallId, content } = config;
1139
1091
  const thread = createThreadManager({ redis, threadId });
@@ -1150,15 +1102,6 @@ function createSharedActivities(redis) {
1150
1102
  async appendHumanMessage(threadId, content) {
1151
1103
  const thread = createThreadManager({ redis, threadId });
1152
1104
  await thread.appendHumanMessage(content);
1153
- },
1154
- async parseToolCalls(storedMessage) {
1155
- const message = mapStoredMessageToChatMessage(storedMessage);
1156
- const toolCalls = message.tool_calls ?? [];
1157
- return toolCalls.map((toolCall) => ({
1158
- id: toolCall.id,
1159
- name: toolCall.name,
1160
- args: toolCall.args
1161
- }));
1162
1105
  }
1163
1106
  };
1164
1107
  }
@@ -1196,9 +1139,14 @@ async function invokeModel({
1196
1139
  }
1197
1140
  );
1198
1141
  await thread.append([response.toDict()]);
1142
+ const toolCalls = response.tool_calls ?? [];
1199
1143
  return {
1200
1144
  message: response.toDict(),
1201
- stopReason: response.response_metadata?.stop_reason ?? null,
1145
+ rawToolCalls: toolCalls.map((tc) => ({
1146
+ id: tc.id,
1147
+ name: tc.name,
1148
+ args: tc.args
1149
+ })),
1202
1150
  usage: {
1203
1151
  input_tokens: response.usage_metadata?.input_tokens,
1204
1152
  output_tokens: response.usage_metadata?.output_tokens,
@@ -1206,7 +1154,7 @@ async function invokeModel({
1206
1154
  }
1207
1155
  };
1208
1156
  }
1209
- var handleAskUserQuestionToolResult = async (args) => {
1157
+ var createAskUserQuestionHandler = () => async (args) => {
1210
1158
  const messages = args.questions.map(
1211
1159
  ({ question, header, options, multiSelect }) => new AIMessage({
1212
1160
  content: question,
@@ -1219,99 +1167,79 @@ var handleAskUserQuestionToolResult = async (args) => {
1219
1167
  );
1220
1168
  return { toolResponse: "Question submitted", data: { chatMessages: messages } };
1221
1169
  };
1222
- async function globHandler(_args, fs) {
1223
- new Bash({ fs });
1224
- return Promise.resolve({
1225
- toolResponse: "Hello, world!",
1226
- data: { files: [] }
1227
- });
1170
+ function createGlobHandler(fs) {
1171
+ return async (_args) => {
1172
+ new Bash({ fs });
1173
+ return {
1174
+ toolResponse: "Hello, world!",
1175
+ data: { files: [] }
1176
+ };
1177
+ };
1228
1178
  }
1229
1179
 
1230
1180
  // src/tools/edit/handler.ts
1231
1181
  function escapeRegExp(str) {
1232
1182
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1233
1183
  }
1234
- async function editHandler(args, fs) {
1235
- const { file_path, old_string, new_string, replace_all = false } = args;
1236
- if (old_string === new_string) {
1237
- return {
1238
- toolResponse: `Error: old_string and new_string must be different.`,
1239
- data: {
1240
- path: file_path,
1241
- success: false,
1242
- replacements: 0
1243
- }
1244
- };
1245
- }
1246
- try {
1247
- const exists = await fs.exists(file_path);
1248
- if (!exists) {
1184
+ function createEditHandler(fs) {
1185
+ return async (args) => {
1186
+ const { file_path, old_string, new_string, replace_all = false } = args;
1187
+ if (old_string === new_string) {
1249
1188
  return {
1250
- toolResponse: `Error: File "${file_path}" does not exist.`,
1251
- data: {
1252
- path: file_path,
1253
- success: false,
1254
- replacements: 0
1255
- }
1189
+ toolResponse: `Error: old_string and new_string must be different.`,
1190
+ data: { path: file_path, success: false, replacements: 0 }
1256
1191
  };
1257
1192
  }
1258
- const content = await fs.readFile(file_path);
1259
- if (!content.includes(old_string)) {
1193
+ try {
1194
+ const exists = await fs.exists(file_path);
1195
+ if (!exists) {
1196
+ return {
1197
+ toolResponse: `Error: File "${file_path}" does not exist.`,
1198
+ data: { path: file_path, success: false, replacements: 0 }
1199
+ };
1200
+ }
1201
+ const content = await fs.readFile(file_path);
1202
+ if (!content.includes(old_string)) {
1203
+ return {
1204
+ toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
1205
+ data: { path: file_path, success: false, replacements: 0 }
1206
+ };
1207
+ }
1208
+ const escapedOldString = escapeRegExp(old_string);
1209
+ const globalRegex = new RegExp(escapedOldString, "g");
1210
+ const occurrences = (content.match(globalRegex) || []).length;
1211
+ if (!replace_all && occurrences > 1) {
1212
+ return {
1213
+ toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
1214
+ data: { path: file_path, success: false, replacements: 0 }
1215
+ };
1216
+ }
1217
+ let newContent;
1218
+ let replacements;
1219
+ if (replace_all) {
1220
+ newContent = content.split(old_string).join(new_string);
1221
+ replacements = occurrences;
1222
+ } else {
1223
+ const index = content.indexOf(old_string);
1224
+ newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
1225
+ replacements = 1;
1226
+ }
1227
+ await fs.writeFile(file_path, newContent);
1228
+ const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
1260
1229
  return {
1261
- toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
1262
- data: {
1263
- path: file_path,
1264
- success: false,
1265
- replacements: 0
1266
- }
1230
+ toolResponse: `${summary} in ${file_path}`,
1231
+ data: { path: file_path, success: true, replacements }
1267
1232
  };
1268
- }
1269
- const escapedOldString = escapeRegExp(old_string);
1270
- const globalRegex = new RegExp(escapedOldString, "g");
1271
- const occurrences = (content.match(globalRegex) || []).length;
1272
- if (!replace_all && occurrences > 1) {
1233
+ } catch (error) {
1234
+ const message = error instanceof Error ? error.message : "Unknown error";
1273
1235
  return {
1274
- toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
1275
- data: {
1276
- path: file_path,
1277
- success: false,
1278
- replacements: 0
1279
- }
1236
+ toolResponse: `Error editing file "${file_path}": ${message}`,
1237
+ data: { path: file_path, success: false, replacements: 0 }
1280
1238
  };
1281
1239
  }
1282
- let newContent;
1283
- let replacements;
1284
- if (replace_all) {
1285
- newContent = content.split(old_string).join(new_string);
1286
- replacements = occurrences;
1287
- } else {
1288
- const index = content.indexOf(old_string);
1289
- newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
1290
- replacements = 1;
1291
- }
1292
- await fs.writeFile(file_path, newContent);
1293
- const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
1294
- return {
1295
- toolResponse: `${summary} in ${file_path}`,
1296
- data: {
1297
- path: file_path,
1298
- success: true,
1299
- replacements
1300
- }
1301
- };
1302
- } catch (error) {
1303
- const message = error instanceof Error ? error.message : "Unknown error";
1304
- return {
1305
- toolResponse: `Error editing file "${file_path}": ${message}`,
1306
- data: {
1307
- path: file_path,
1308
- success: false,
1309
- replacements: 0
1310
- }
1311
- };
1312
- }
1240
+ };
1313
1241
  }
1314
- var handleBashTool = (bashOptions) => async (args, _context) => {
1242
+ var createBashHandler = (bashOptions) => async (args, _context) => {
1315
1243
  const { command } = args;
1316
1244
  const mergedOptions = {
1317
1245
  ...bashOptions,
@@ -1408,6 +1336,6 @@ var toTree = async (fs, opts = {}) => {
1408
1336
  return base + subtree;
1409
1337
  };
1410
1338
 
1411
- export { AGENT_HANDLER_NAMES, ZeitlichPlugin, askUserQuestionTool, bashTool, createAgentStateManager, createSession, createSharedActivities, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskTool, createTaskUpdateHandler, createThreadManager, createToolRouter, defineSubagent, defineTool, editHandler, editTool, getStateQuery, globHandler, globTool, grepTool, handleAskUserQuestionToolResult, handleBashTool, hasNoOtherToolCalls, invokeModel, isTerminalStatus, readTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, writeTool };
1339
+ export { AGENT_HANDLER_NAMES, ZeitlichPlugin, askUserQuestionTool, bashTool, createAgentStateManager, createAskUserQuestionHandler, createBashHandler, createBashToolDescription, createEditHandler, createGlobHandler, createSession, createSharedActivities, createSubagentTool, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskUpdateHandler, createThreadManager, createToolRouter, defineSubagent, defineTool, editTool, getStateQuery, globTool, grepTool, hasNoOtherToolCalls, invokeModel, isTerminalStatus, proxyDefaultThreadOps, readTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, writeTool };
1412
1340
  //# sourceMappingURL=index.js.map
1413
1341
  //# sourceMappingURL=index.js.map