zeitlich 0.2.2 → 0.2.3

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 (45) hide show
  1. package/README.md +34 -31
  2. package/dist/index.cjs +305 -361
  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 +277 -336
  7. package/dist/index.js.map +1 -1
  8. package/dist/{workflow-BQf5EfNN.d.cts → workflow-D-2vp4Pq.d.cts} +265 -241
  9. package/dist/{workflow-BQf5EfNN.d.ts → workflow-D-2vp4Pq.d.ts} +265 -241
  10. package/dist/workflow.cjs +206 -253
  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 +182 -231
  15. package/dist/workflow.js.map +1 -1
  16. package/package.json +3 -2
  17. package/src/activities.ts +1 -14
  18. package/src/index.ts +14 -11
  19. package/src/lib/session.ts +56 -99
  20. package/src/lib/thread-manager.ts +45 -37
  21. package/src/lib/tool-router.ts +143 -103
  22. package/src/lib/types.ts +32 -25
  23. package/src/tools/ask-user-question/handler.ts +5 -5
  24. package/src/tools/ask-user-question/tool.ts +3 -2
  25. package/src/tools/bash/bash.test.ts +12 -12
  26. package/src/tools/bash/handler.ts +5 -5
  27. package/src/tools/bash/tool.ts +3 -2
  28. package/src/tools/edit/handler.ts +78 -123
  29. package/src/tools/edit/tool.ts +3 -2
  30. package/src/tools/glob/handler.ts +17 -48
  31. package/src/tools/glob/tool.ts +3 -2
  32. package/src/tools/grep/tool.ts +3 -2
  33. package/src/tools/{read → read-file}/tool.ts +3 -2
  34. package/src/tools/task/handler.ts +2 -2
  35. package/src/tools/task/tool.ts +2 -9
  36. package/src/tools/task-create/handler.ts +5 -11
  37. package/src/tools/task-create/tool.ts +3 -2
  38. package/src/tools/task-get/handler.ts +5 -10
  39. package/src/tools/task-get/tool.ts +3 -2
  40. package/src/tools/task-list/handler.ts +5 -10
  41. package/src/tools/task-list/tool.ts +3 -2
  42. package/src/tools/task-update/handler.ts +5 -12
  43. package/src/tools/task-update/tool.ts +3 -2
  44. package/src/tools/{write → write-file}/tool.ts +5 -6
  45. package/src/workflow.ts +23 -19
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var workflow = require('@temporalio/workflow');
4
- var z5 = require('zod');
4
+ var z3 = require('zod');
5
5
  var plugin = require('@temporalio/plugin');
6
6
  var messages = require('@langchain/core/messages');
7
7
  var crypto = require('crypto');
@@ -10,7 +10,7 @@ var justBash = require('just-bash');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
12
 
13
- var z5__default = /*#__PURE__*/_interopDefault(z5);
13
+ var z3__default = /*#__PURE__*/_interopDefault(z3);
14
14
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
15
15
 
16
16
  // src/lib/session.ts
@@ -46,10 +46,10 @@ function createTaskTool(subagents) {
46
46
  return {
47
47
  name: TASK_TOOL,
48
48
  description: buildTaskDescription(subagents),
49
- schema: z5__default.default.object({
50
- subagent: z5__default.default.enum(names).describe("The type of subagent to launch"),
51
- description: z5__default.default.string().describe("A short (3-5 word) description of the task"),
52
- prompt: z5__default.default.string().describe("The task for the agent to perform")
49
+ schema: z3__default.default.object({
50
+ subagent: z3__default.default.enum(names).describe("The type of subagent to launch"),
51
+ description: z3__default.default.string().describe("A short (3-5 word) description of the task"),
52
+ prompt: z3__default.default.string().describe("The task for the agent to perform")
53
53
  })
54
54
  };
55
55
  }
@@ -84,107 +84,15 @@ function createTaskHandler(subagents) {
84
84
  };
85
85
  };
86
86
  }
87
- var createBashToolDescription = ({
88
- fileTree
89
- }) => `Execute shell commands in a bash environment.
90
-
91
- Use this tool to:
92
- - Run shell commands (ls, cat, grep, find, etc.)
93
- - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
94
- - Inspect files and directories
95
-
96
- Current file tree:
97
- ${fileTree}`;
98
- var bashTool = {
99
- name: "Bash",
100
- description: `Execute shell commands in a sandboxed bash environment.
101
-
102
- Use this tool to:
103
- - Run shell commands (ls, cat, grep, find, etc.)
104
- - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
105
- - Inspect files and directories
106
- `,
107
- schema: z5__default.default.object({
108
- command: z5__default.default.string().describe(
109
- "The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
110
- )
111
- }),
112
- strict: true
113
- };
114
- var taskCreateTool = {
115
- name: "TaskCreate",
116
- 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.
117
- It also helps the user understand the progress of the task and overall progress of their requests.
118
-
119
- ## When to Use This Tool
120
-
121
- Use this tool proactively in these scenarios:
122
-
123
- - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
124
- - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
125
- - User explicitly requests todo list - When the user directly asks you to use the todo list
126
- - User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
127
- - After receiving new instructions - Immediately capture user requirements as tasks
128
- - When you start working on a task - Mark it as in_progress BEFORE beginning work
129
- - After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
130
-
131
- ## When NOT to Use This Tool
132
-
133
- Skip using this tool when:
134
- - There is only a single, straightforward task
135
- - The task is trivial and tracking it provides no organizational benefit
136
- - The task can be completed in less than 3 trivial steps
137
- - The task is purely conversational or informational
138
-
139
- 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.
140
-
141
- ## Task Fields
142
-
143
- - **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
144
- - **description**: Detailed description of what needs to be done, including context and acceptance criteria
145
- - **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.
146
-
147
- **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\`.
148
-
149
- ## Tips
150
-
151
- - Create tasks with clear, specific subjects that describe the outcome
152
- - Include enough detail in the description for another agent to understand and complete the task
153
- - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
154
- - Check TaskList first to avoid creating duplicate tasks`,
155
- schema: z5__default.default.object({
156
- subject: z5__default.default.string().describe(
157
- 'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
158
- ),
159
- description: z5__default.default.string().describe(
160
- "Detailed description of what needs to be done, including context and acceptance criteria"
161
- ),
162
- activeForm: z5__default.default.string().describe(
163
- '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.'
164
- ),
165
- metadata: z5__default.default.record(z5__default.default.string(), z5__default.default.string()).describe("Arbitrary key-value pairs for tracking")
166
- })
167
- };
168
87
 
169
88
  // src/lib/tool-router.ts
170
- var buildIntoolDefinitions = {
171
- [bashTool.name]: bashTool,
172
- [taskCreateTool.name]: taskCreateTool
173
- };
174
89
  function createToolRouter(options) {
175
- const { appendToolResult } = workflow.proxyActivities({
176
- startToCloseTimeout: "2m",
177
- retry: {
178
- maximumAttempts: 3,
179
- initialInterval: "5s",
180
- maximumInterval: "15m",
181
- backoffCoefficient: 4
182
- }
183
- });
90
+ const { appendToolResult } = options;
184
91
  const toolMap = /* @__PURE__ */ new Map();
185
92
  for (const [_key, tool] of Object.entries(options.tools)) {
186
93
  toolMap.set(tool.name, tool);
187
94
  }
95
+ const isEnabled = (tool) => tool.enabled !== false;
188
96
  if (options.subagents) {
189
97
  const subagentHooksMap = /* @__PURE__ */ new Map();
190
98
  for (const s of options.subagents) {
@@ -212,24 +120,6 @@ function createToolRouter(options) {
212
120
  }
213
121
  });
214
122
  }
215
- if (options.buildInTools) {
216
- for (const [key, value] of Object.entries(options.buildInTools)) {
217
- if (key === bashTool.name) {
218
- toolMap.set(key, {
219
- ...buildIntoolDefinitions[key],
220
- description: createBashToolDescription({
221
- fileTree: options.fileTree
222
- }),
223
- handler: value
224
- });
225
- } else {
226
- toolMap.set(key, {
227
- ...buildIntoolDefinitions[key],
228
- handler: value
229
- });
230
- }
231
- }
232
- }
233
123
  async function processToolCall(toolCall, turn, handlerContext) {
234
124
  const startTime = Date.now();
235
125
  const tool = toolMap.get(toolCall.name);
@@ -245,6 +135,7 @@ function createToolRouter(options) {
245
135
  await appendToolResult({
246
136
  threadId: options.threadId,
247
137
  toolCallId: toolCall.id,
138
+ toolName: toolCall.name,
248
139
  content: JSON.stringify({
249
140
  skipped: true,
250
141
  reason: "Skipped by PreToolUse hook"
@@ -266,6 +157,7 @@ function createToolRouter(options) {
266
157
  await appendToolResult({
267
158
  threadId: options.threadId,
268
159
  toolCallId: toolCall.id,
160
+ toolName: toolCall.name,
269
161
  content: JSON.stringify({
270
162
  skipped: true,
271
163
  reason: "Skipped by tool PreToolUse hook"
@@ -279,14 +171,22 @@ function createToolRouter(options) {
279
171
  }
280
172
  let result;
281
173
  let content;
174
+ let resultAppended = false;
282
175
  try {
283
176
  if (tool) {
177
+ const enrichedContext = {
178
+ ...handlerContext ?? {},
179
+ threadId: options.threadId,
180
+ toolCallId: toolCall.id,
181
+ toolName: toolCall.name
182
+ };
284
183
  const response = await tool.handler(
285
184
  effectiveArgs,
286
- handlerContext ?? {}
185
+ enrichedContext
287
186
  );
288
187
  result = response.data;
289
188
  content = response.toolResponse;
189
+ resultAppended = response.resultAppended === true;
290
190
  } else {
291
191
  result = { error: `Unknown tool: ${toolCall.name}` };
292
192
  content = JSON.stringify(result, null, 2);
@@ -332,11 +232,14 @@ function createToolRouter(options) {
332
232
  throw error;
333
233
  }
334
234
  }
335
- await appendToolResult({
336
- threadId: options.threadId,
337
- toolCallId: toolCall.id,
338
- content
339
- });
235
+ if (!resultAppended) {
236
+ await appendToolResult({
237
+ threadId: options.threadId,
238
+ toolCallId: toolCall.id,
239
+ toolName: toolCall.name,
240
+ content
241
+ });
242
+ }
340
243
  const toolResult = {
341
244
  toolCallId: toolCall.id,
342
245
  name: toolCall.name,
@@ -366,11 +269,11 @@ function createToolRouter(options) {
366
269
  return {
367
270
  // --- Methods from registry ---
368
271
  hasTools() {
369
- return toolMap.size > 0;
272
+ return Array.from(toolMap.values()).some(isEnabled);
370
273
  },
371
274
  parseToolCall(toolCall) {
372
275
  const tool = toolMap.get(toolCall.name);
373
- if (!tool) {
276
+ if (!tool || !isEnabled(tool)) {
374
277
  throw new Error(`Tool ${toolCall.name} not found`);
375
278
  }
376
279
  const parsedArgs = tool.schema.parse(toolCall.args);
@@ -381,13 +284,14 @@ function createToolRouter(options) {
381
284
  };
382
285
  },
383
286
  hasTool(name) {
384
- return toolMap.has(name);
287
+ const tool = toolMap.get(name);
288
+ return tool !== void 0 && isEnabled(tool);
385
289
  },
386
290
  getToolNames() {
387
- return Array.from(toolMap.keys());
291
+ return Array.from(toolMap.entries()).filter(([, tool]) => isEnabled(tool)).map(([name]) => name);
388
292
  },
389
293
  getToolDefinitions() {
390
- return Array.from(toolMap).map(([name, tool]) => ({
294
+ return Array.from(toolMap).filter(([, tool]) => isEnabled(tool)).map(([name, tool]) => ({
391
295
  name,
392
296
  description: tool.description,
393
297
  schema: tool.schema,
@@ -426,19 +330,28 @@ function createToolRouter(options) {
426
330
  }
427
331
  const handlerContext = context?.handlerContext ?? {};
428
332
  const processOne = async (toolCall) => {
333
+ const enrichedContext = {
334
+ ...handlerContext ?? {},
335
+ threadId: options.threadId,
336
+ toolCallId: toolCall.id,
337
+ toolName: toolCall.name
338
+ };
429
339
  const response = await handler(
430
340
  toolCall.args,
431
- handlerContext
341
+ enrichedContext
432
342
  );
433
- await appendToolResult({
434
- threadId: options.threadId,
435
- toolCallId: toolCall.id,
436
- content: response.toolResponse
437
- });
343
+ if (!response.resultAppended) {
344
+ await appendToolResult({
345
+ threadId: options.threadId,
346
+ toolCallId: toolCall.id,
347
+ toolName: toolCall.name,
348
+ content: response.toolResponse
349
+ });
350
+ }
438
351
  return {
439
352
  toolCallId: toolCall.id,
440
353
  name: toolCall.name,
441
- data: response.data ?? null
354
+ data: response.data
442
355
  };
443
356
  };
444
357
  if (options.parallel) {
@@ -464,6 +377,21 @@ function createToolRouter(options) {
464
377
  }
465
378
  };
466
379
  }
380
+ function withAutoAppend(threadHandler, handler) {
381
+ return async (args, context) => {
382
+ const response = await handler(args, context);
383
+ const threadId = context.threadId;
384
+ const toolCallId = context.toolCallId;
385
+ const toolName = context.toolName;
386
+ await threadHandler({
387
+ threadId,
388
+ toolCallId,
389
+ toolName,
390
+ content: response.toolResponse
391
+ });
392
+ return { toolResponse: "", data: response.data, resultAppended: true };
393
+ };
394
+ }
467
395
  function defineTool(tool) {
468
396
  return tool;
469
397
  }
@@ -475,51 +403,24 @@ function hasNoOtherToolCalls(toolCalls, excludeName) {
475
403
  }
476
404
 
477
405
  // src/lib/session.ts
478
- async function resolvePrompt(prompt) {
479
- if (typeof prompt === "function") {
480
- return prompt();
481
- }
482
- return prompt;
483
- }
484
406
  var createSession = async ({
485
407
  threadId,
486
408
  agentName,
487
409
  maxTurns = 50,
488
410
  metadata = {},
489
411
  runAgent,
490
- baseSystemPrompt,
491
- instructionsPrompt,
412
+ threadOps,
492
413
  buildContextMessage,
493
- buildFileTree = async () => "",
494
414
  subagents,
495
415
  tools = {},
496
416
  processToolsInParallel = true,
497
- buildInTools = {},
498
417
  hooks = {}
499
418
  }) => {
500
- const {
501
- initializeThread,
502
- appendHumanMessage,
503
- parseToolCalls,
504
- appendToolResult,
505
- appendSystemMessage
506
- } = workflow.proxyActivities({
507
- startToCloseTimeout: "30m",
508
- retry: {
509
- maximumAttempts: 6,
510
- initialInterval: "5s",
511
- maximumInterval: "15m",
512
- backoffCoefficient: 4
513
- },
514
- heartbeatTimeout: "5m"
515
- });
516
- const fileTree = await buildFileTree();
517
419
  const toolRouter = createToolRouter({
518
420
  tools,
421
+ appendToolResult: threadOps.appendToolResult,
519
422
  threadId,
520
423
  hooks,
521
- buildInTools,
522
- fileTree,
523
424
  subagents,
524
425
  parallel: processToolsInParallel
525
426
  });
@@ -544,15 +445,8 @@ var createSession = async ({
544
445
  });
545
446
  }
546
447
  stateManager.setTools(toolRouter.getToolDefinitions());
547
- await initializeThread(threadId);
548
- await appendSystemMessage(
549
- threadId,
550
- [
551
- await resolvePrompt(baseSystemPrompt),
552
- await resolvePrompt(instructionsPrompt)
553
- ].join("\n")
554
- );
555
- await appendHumanMessage(threadId, await buildContextMessage());
448
+ await threadOps.initializeThread(threadId);
449
+ await threadOps.appendHumanMessage(threadId, await buildContextMessage());
556
450
  let exitReason = "completed";
557
451
  try {
558
452
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
@@ -573,54 +467,25 @@ var createSession = async ({
573
467
  exitReason = "completed";
574
468
  return message;
575
469
  }
576
- const rawToolCalls = await parseToolCalls(message);
470
+ const rawToolCalls = await threadOps.parseToolCalls(message);
577
471
  const parsedToolCalls = [];
578
- for (const tc of rawToolCalls.filter(
579
- (tc2) => tc2.name !== "Task"
580
- )) {
472
+ for (const tc of rawToolCalls) {
581
473
  try {
582
474
  parsedToolCalls.push(toolRouter.parseToolCall(tc));
583
475
  } catch (error) {
584
- await appendToolResult({
476
+ await threadOps.appendToolResult({
585
477
  threadId,
586
478
  toolCallId: tc.id ?? "",
479
+ toolName: tc.name,
587
480
  content: JSON.stringify({
588
481
  error: `Invalid tool call for "${tc.name}": ${error instanceof Error ? error.message : String(error)}`
589
482
  })
590
483
  });
591
484
  }
592
485
  }
593
- const taskToolCalls = [];
594
- if (subagents && subagents.length > 0) {
595
- for (const tc of rawToolCalls.filter(
596
- (tc2) => tc2.name === "Task"
597
- )) {
598
- try {
599
- const parsedArgs = createTaskTool(subagents).schema.parse(
600
- tc.args
601
- );
602
- taskToolCalls.push({
603
- id: tc.id ?? "",
604
- name: tc.name,
605
- args: parsedArgs
606
- });
607
- } catch (error) {
608
- await appendToolResult({
609
- threadId,
610
- toolCallId: tc.id ?? "",
611
- content: JSON.stringify({
612
- error: `Invalid tool call for "Task": ${error instanceof Error ? error.message : String(error)}`
613
- })
614
- });
615
- }
616
- }
617
- }
618
- await toolRouter.processToolCalls(
619
- [...parsedToolCalls, ...taskToolCalls],
620
- {
621
- turn: currentTurn
622
- }
623
- );
486
+ await toolRouter.processToolCalls(parsedToolCalls, {
487
+ turn: currentTurn
488
+ });
624
489
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
625
490
  exitReason = "waiting_for_input";
626
491
  break;
@@ -639,6 +504,26 @@ var createSession = async ({
639
504
  }
640
505
  };
641
506
  };
507
+ function proxyDefaultThreadOps(options) {
508
+ const activities = workflow.proxyActivities(
509
+ options ?? {
510
+ startToCloseTimeout: "30m",
511
+ retry: {
512
+ maximumAttempts: 6,
513
+ initialInterval: "5s",
514
+ maximumInterval: "15m",
515
+ backoffCoefficient: 4
516
+ },
517
+ heartbeatTimeout: "5m"
518
+ }
519
+ );
520
+ return {
521
+ initializeThread: activities.initializeThread,
522
+ appendHumanMessage: activities.appendHumanMessage,
523
+ appendToolResult: activities.appendToolResult,
524
+ parseToolCalls: activities.parseToolCalls
525
+ };
526
+ }
642
527
 
643
528
  // src/lib/types.ts
644
529
  function isTerminalStatus(status) {
@@ -741,7 +626,7 @@ function createAgentStateManager(initialState) {
741
626
  tools = newTools.map((tool) => ({
742
627
  name: tool.name,
743
628
  description: tool.description,
744
- schema: z5.z.toJSONSchema(tool.schema),
629
+ schema: z3.z.toJSONSchema(tool.schema),
745
630
  strict: tool.strict,
746
631
  max_uses: tool.max_uses
747
632
  }));
@@ -775,18 +660,18 @@ Usage notes:
775
660
  * Use multiSelect: true to allow multiple answers to be selected for a question
776
661
  * If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
777
662
  `,
778
- schema: z5__default.default.object({
779
- questions: z5__default.default.array(
780
- z5__default.default.object({
781
- question: z5__default.default.string().describe("The full question text to display"),
782
- header: z5__default.default.string().describe("Short label for the question (max 12 characters)"),
783
- options: z5__default.default.array(
784
- z5__default.default.object({
785
- label: z5__default.default.string(),
786
- description: z5__default.default.string()
663
+ schema: z3__default.default.object({
664
+ questions: z3__default.default.array(
665
+ z3__default.default.object({
666
+ question: z3__default.default.string().describe("The full question text to display"),
667
+ header: z3__default.default.string().describe("Short label for the question (max 12 characters)"),
668
+ options: z3__default.default.array(
669
+ z3__default.default.object({
670
+ label: z3__default.default.string(),
671
+ description: z3__default.default.string()
787
672
  })
788
673
  ).min(0).max(4).describe("Array of 0-4 choices, each with label and description"),
789
- multiSelect: z5__default.default.boolean().describe("If true, users can select multiple options")
674
+ multiSelect: z3__default.default.boolean().describe("If true, users can select multiple options")
790
675
  })
791
676
  )
792
677
  }),
@@ -806,9 +691,9 @@ Examples:
806
691
  - "**/*.test.ts" - Find all test files recursively
807
692
  - "src/**/*.ts" - Find all TypeScript files in src directory
808
693
  `,
809
- schema: z5.z.object({
810
- pattern: z5.z.string().describe("Glob pattern to match files against"),
811
- root: z5.z.string().optional().describe("Optional root directory to search from")
694
+ schema: z3.z.object({
695
+ pattern: z3.z.string().describe("Glob pattern to match files against"),
696
+ root: z3.z.string().optional().describe("Optional root directory to search from")
812
697
  }),
813
698
  strict: true
814
699
  };
@@ -826,13 +711,13 @@ Examples:
826
711
  - Search for function definitions with "function.*handleClick"
827
712
  - Search case-insensitively with ignoreCase: true
828
713
  `,
829
- schema: z5.z.object({
830
- pattern: z5.z.string().describe("Regex pattern to search for in file contents"),
831
- ignoreCase: z5.z.boolean().optional().describe("Case-insensitive search (default: false)"),
832
- maxMatches: z5.z.number().optional().describe("Maximum number of matches to return (default: 50)"),
833
- includePatterns: z5.z.array(z5.z.string()).optional().describe("Glob patterns to include (e.g., ['*.ts', '*.js'])"),
834
- excludePatterns: z5.z.array(z5.z.string()).optional().describe("Glob patterns to exclude (e.g., ['*.test.ts'])"),
835
- contextLines: z5.z.number().optional().describe("Number of context lines to show around matches")
714
+ schema: z3.z.object({
715
+ pattern: z3.z.string().describe("Regex pattern to search for in file contents"),
716
+ ignoreCase: z3.z.boolean().optional().describe("Case-insensitive search (default: false)"),
717
+ maxMatches: z3.z.number().optional().describe("Maximum number of matches to return (default: 50)"),
718
+ includePatterns: z3.z.array(z3.z.string()).optional().describe("Glob patterns to include (e.g., ['*.ts', '*.js'])"),
719
+ excludePatterns: z3.z.array(z3.z.string()).optional().describe("Glob patterns to exclude (e.g., ['*.test.ts'])"),
720
+ contextLines: z3.z.number().optional().describe("Number of context lines to show around matches")
836
721
  }),
837
722
  strict: true
838
723
  };
@@ -850,12 +735,12 @@ The tool returns the file content in an appropriate format:
850
735
  - Images: Base64-encoded image data
851
736
  - PDFs: Extracted text content
852
737
  `,
853
- schema: z5.z.object({
854
- path: z5.z.string().describe("Virtual path to the file to read"),
855
- offset: z5.z.number().optional().describe(
738
+ schema: z3.z.object({
739
+ path: z3.z.string().describe("Virtual path to the file to read"),
740
+ offset: z3.z.number().optional().describe(
856
741
  "Line number to start reading from (1-indexed, for text files)"
857
742
  ),
858
- limit: z5.z.number().optional().describe("Maximum number of lines to read (for text files)")
743
+ limit: z3.z.number().optional().describe("Maximum number of lines to read (for text files)")
859
744
  }),
860
745
  strict: true
861
746
  };
@@ -864,7 +749,7 @@ var writeTool = {
864
749
  description: `Create or overwrite a file with new content.
865
750
 
866
751
  Usage:
867
- - Provide the absolute virtual path to the file
752
+ - Provide the absolute path to the file
868
753
  - The file will be created if it doesn't exist
869
754
  - If the file exists, it will be completely overwritten
870
755
 
@@ -873,9 +758,9 @@ IMPORTANT:
873
758
  - This is an atomic write operation - the entire file is replaced
874
759
  - Path must be absolute (e.g., "/docs/readme.md", not "docs/readme.md")
875
760
  `,
876
- schema: z5.z.object({
877
- file_path: z5.z.string().describe("The absolute virtual path to the file to write"),
878
- content: z5.z.string().describe("The content to write to the file")
761
+ schema: z3.z.object({
762
+ file_path: z3.z.string().describe("The absolute path to the file to write"),
763
+ content: z3.z.string().describe("The content to write to the file")
879
764
  }),
880
765
  strict: true
881
766
  };
@@ -895,18 +780,72 @@ IMPORTANT:
895
780
  - The operation fails if old_string is not found
896
781
  - old_string and new_string must be different
897
782
  `,
898
- schema: z5.z.object({
899
- file_path: z5.z.string().describe("The absolute virtual path to the file to modify"),
900
- old_string: z5.z.string().describe("The exact text to replace"),
901
- new_string: z5.z.string().describe(
783
+ schema: z3.z.object({
784
+ file_path: z3.z.string().describe("The absolute virtual path to the file to modify"),
785
+ old_string: z3.z.string().describe("The exact text to replace"),
786
+ new_string: z3.z.string().describe(
902
787
  "The text to replace it with (must be different from old_string)"
903
788
  ),
904
- replace_all: z5.z.boolean().optional().describe(
789
+ replace_all: z3.z.boolean().optional().describe(
905
790
  "If true, replace all occurrences of old_string (default: false)"
906
791
  )
907
792
  }),
908
793
  strict: true
909
794
  };
795
+ var taskCreateTool = {
796
+ name: "TaskCreate",
797
+ 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.
798
+ It also helps the user understand the progress of the task and overall progress of their requests.
799
+
800
+ ## When to Use This Tool
801
+
802
+ Use this tool proactively in these scenarios:
803
+
804
+ - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
805
+ - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
806
+ - User explicitly requests todo list - When the user directly asks you to use the todo list
807
+ - User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
808
+ - After receiving new instructions - Immediately capture user requirements as tasks
809
+ - When you start working on a task - Mark it as in_progress BEFORE beginning work
810
+ - After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
811
+
812
+ ## When NOT to Use This Tool
813
+
814
+ Skip using this tool when:
815
+ - There is only a single, straightforward task
816
+ - The task is trivial and tracking it provides no organizational benefit
817
+ - The task can be completed in less than 3 trivial steps
818
+ - The task is purely conversational or informational
819
+
820
+ 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.
821
+
822
+ ## Task Fields
823
+
824
+ - **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
825
+ - **description**: Detailed description of what needs to be done, including context and acceptance criteria
826
+ - **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.
827
+
828
+ **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\`.
829
+
830
+ ## Tips
831
+
832
+ - Create tasks with clear, specific subjects that describe the outcome
833
+ - Include enough detail in the description for another agent to understand and complete the task
834
+ - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
835
+ - Check TaskList first to avoid creating duplicate tasks`,
836
+ schema: z3__default.default.object({
837
+ subject: z3__default.default.string().describe(
838
+ 'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
839
+ ),
840
+ description: z3__default.default.string().describe(
841
+ "Detailed description of what needs to be done, including context and acceptance criteria"
842
+ ),
843
+ activeForm: z3__default.default.string().describe(
844
+ '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.'
845
+ ),
846
+ metadata: z3__default.default.record(z3__default.default.string(), z3__default.default.string()).describe("Arbitrary key-value pairs for tracking")
847
+ })
848
+ };
910
849
  function createTaskCreateHandler(stateManager) {
911
850
  return (args) => {
912
851
  const task = {
@@ -929,8 +868,8 @@ function createTaskCreateHandler(stateManager) {
929
868
  var taskGetTool = {
930
869
  name: "TaskGet",
931
870
  description: `Retrieve full task details including dependencies.`,
932
- schema: z5__default.default.object({
933
- taskId: z5__default.default.string().describe("The ID of the task to get")
871
+ schema: z3__default.default.object({
872
+ taskId: z3__default.default.string().describe("The ID of the task to get")
934
873
  })
935
874
  };
936
875
 
@@ -953,12 +892,12 @@ function createTaskGetHandler(stateManager) {
953
892
  var taskListTool = {
954
893
  name: "TaskList",
955
894
  description: `List all tasks with current state.`,
956
- schema: z5__default.default.object({})
895
+ schema: z3__default.default.object({})
957
896
  };
958
897
 
959
898
  // src/tools/task-list/handler.ts
960
899
  function createTaskListHandler(stateManager) {
961
- return (_args) => {
900
+ return () => {
962
901
  const taskList = stateManager.getTasks();
963
902
  return {
964
903
  toolResponse: JSON.stringify(taskList, null, 2),
@@ -969,11 +908,11 @@ function createTaskListHandler(stateManager) {
969
908
  var taskUpdateTool = {
970
909
  name: "TaskUpdate",
971
910
  description: `Update status, add blockers, modify details.`,
972
- schema: z5__default.default.object({
973
- taskId: z5__default.default.string().describe("The ID of the task to get"),
974
- status: z5__default.default.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
975
- addBlockedBy: z5__default.default.array(z5__default.default.string()).describe("The IDs of the tasks that are blocking this task"),
976
- addBlocks: z5__default.default.array(z5__default.default.string()).describe("The IDs of the tasks that this task is blocking")
911
+ schema: z3__default.default.object({
912
+ taskId: z3__default.default.string().describe("The ID of the task to get"),
913
+ status: z3__default.default.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
914
+ addBlockedBy: z3__default.default.array(z3__default.default.string()).describe("The IDs of the tasks that are blocking this task"),
915
+ addBlocks: z3__default.default.array(z3__default.default.string()).describe("The IDs of the tasks that this task is blocking")
977
916
  })
978
917
  };
979
918
 
@@ -1021,6 +960,33 @@ function createTaskUpdateHandler(stateManager) {
1021
960
  };
1022
961
  };
1023
962
  }
963
+ var createBashToolDescription = ({
964
+ fileTree
965
+ }) => `Execute shell commands in a bash environment.
966
+
967
+ Use this tool to:
968
+ - Run shell commands (ls, cat, grep, find, etc.)
969
+ - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
970
+ - Inspect files and directories
971
+
972
+ Current file tree:
973
+ ${fileTree}`;
974
+ var bashTool = {
975
+ name: "Bash",
976
+ description: `Execute shell commands in a sandboxed bash environment.
977
+
978
+ Use this tool to:
979
+ - Run shell commands (ls, cat, grep, find, etc.)
980
+ - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
981
+ - Inspect files and directories
982
+ `,
983
+ schema: z3__default.default.object({
984
+ command: z3__default.default.string().describe(
985
+ "The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
986
+ )
987
+ }),
988
+ strict: true
989
+ };
1024
990
 
1025
991
  // node_modules/uuid/dist/esm-node/stringify.js
1026
992
  var byteToHex = [];
@@ -1069,25 +1035,33 @@ function getThreadKey(threadId, key) {
1069
1035
  return `thread:${threadId}:${key}`;
1070
1036
  }
1071
1037
  function createThreadManager(config) {
1072
- const { redis, threadId, key = "messages" } = config;
1038
+ const {
1039
+ redis,
1040
+ threadId,
1041
+ key = "messages",
1042
+ serialize = (m) => JSON.stringify(m),
1043
+ deserialize = (raw) => JSON.parse(raw)
1044
+ } = config;
1073
1045
  const redisKey = getThreadKey(threadId, key);
1074
- return {
1046
+ const base = {
1075
1047
  async initialize() {
1076
1048
  await redis.del(redisKey);
1077
1049
  },
1078
1050
  async load() {
1079
1051
  const data = await redis.lrange(redisKey, 0, -1);
1080
- return data.map((item) => JSON.parse(item));
1052
+ return data.map(deserialize);
1081
1053
  },
1082
1054
  async append(messages) {
1083
1055
  if (messages.length > 0) {
1084
- await redis.rpush(redisKey, ...messages.map((m) => JSON.stringify(m)));
1056
+ await redis.rpush(redisKey, ...messages.map(serialize));
1085
1057
  await redis.expire(redisKey, THREAD_TTL_SECONDS);
1086
1058
  }
1087
1059
  },
1088
1060
  async delete() {
1089
1061
  await redis.del(redisKey);
1090
- },
1062
+ }
1063
+ };
1064
+ const helpers = {
1091
1065
  createHumanMessage(content) {
1092
1066
  return new messages.HumanMessage({
1093
1067
  id: v4_default(),
@@ -1107,40 +1081,27 @@ function createThreadManager(config) {
1107
1081
  },
1108
1082
  createToolMessage(content, toolCallId) {
1109
1083
  return new messages.ToolMessage({
1110
- // Cast needed due to langchain type compatibility
1111
1084
  content,
1112
1085
  tool_call_id: toolCallId
1113
1086
  }).toDict();
1114
1087
  },
1115
- createSystemMessage(content) {
1116
- return new messages.SystemMessage({
1117
- content
1118
- }).toDict();
1119
- },
1120
- async appendSystemMessage(content) {
1121
- const message = this.createSystemMessage(content);
1122
- await this.append([message]);
1123
- },
1124
1088
  async appendHumanMessage(content) {
1125
- const message = this.createHumanMessage(content);
1126
- await this.append([message]);
1089
+ const message = helpers.createHumanMessage(content);
1090
+ await base.append([message]);
1127
1091
  },
1128
1092
  async appendToolMessage(content, toolCallId) {
1129
- const message = this.createToolMessage(content, toolCallId);
1130
- await this.append([message]);
1093
+ const message = helpers.createToolMessage(content, toolCallId);
1094
+ await base.append([message]);
1131
1095
  },
1132
1096
  async appendAIMessage(content) {
1133
- const message = this.createAIMessage(content);
1134
- await this.append([message]);
1097
+ const message = helpers.createAIMessage(content);
1098
+ await base.append([message]);
1135
1099
  }
1136
1100
  };
1101
+ return Object.assign(base, helpers);
1137
1102
  }
1138
1103
  function createSharedActivities(redis) {
1139
1104
  return {
1140
- async appendSystemMessage(threadId, content) {
1141
- const thread = createThreadManager({ redis, threadId });
1142
- await thread.appendSystemMessage(content);
1143
- },
1144
1105
  async appendToolResult(config) {
1145
1106
  const { threadId, toolCallId, content } = config;
1146
1107
  const thread = createThreadManager({ redis, threadId });
@@ -1213,7 +1174,7 @@ async function invokeModel({
1213
1174
  }
1214
1175
  };
1215
1176
  }
1216
- var handleAskUserQuestionToolResult = async (args) => {
1177
+ var createAskUserQuestionHandler = () => async (args) => {
1217
1178
  const messages$1 = args.questions.map(
1218
1179
  ({ question, header, options, multiSelect }) => new messages.AIMessage({
1219
1180
  content: question,
@@ -1226,99 +1187,79 @@ var handleAskUserQuestionToolResult = async (args) => {
1226
1187
  );
1227
1188
  return { toolResponse: "Question submitted", data: { chatMessages: messages$1 } };
1228
1189
  };
1229
- async function globHandler(_args, fs) {
1230
- new justBash.Bash({ fs });
1231
- return Promise.resolve({
1232
- toolResponse: "Hello, world!",
1233
- data: { files: [] }
1234
- });
1190
+ function createGlobHandler(fs) {
1191
+ return async (_args) => {
1192
+ new justBash.Bash({ fs });
1193
+ return {
1194
+ toolResponse: "Hello, world!",
1195
+ data: { files: [] }
1196
+ };
1197
+ };
1235
1198
  }
1236
1199
 
1237
1200
  // src/tools/edit/handler.ts
1238
1201
  function escapeRegExp(str) {
1239
1202
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1240
1203
  }
1241
- async function editHandler(args, fs) {
1242
- const { file_path, old_string, new_string, replace_all = false } = args;
1243
- if (old_string === new_string) {
1244
- return {
1245
- toolResponse: `Error: old_string and new_string must be different.`,
1246
- data: {
1247
- path: file_path,
1248
- success: false,
1249
- replacements: 0
1250
- }
1251
- };
1252
- }
1253
- try {
1254
- const exists = await fs.exists(file_path);
1255
- if (!exists) {
1204
+ function createEditHandler(fs) {
1205
+ return async (args) => {
1206
+ const { file_path, old_string, new_string, replace_all = false } = args;
1207
+ if (old_string === new_string) {
1256
1208
  return {
1257
- toolResponse: `Error: File "${file_path}" does not exist.`,
1258
- data: {
1259
- path: file_path,
1260
- success: false,
1261
- replacements: 0
1262
- }
1209
+ toolResponse: `Error: old_string and new_string must be different.`,
1210
+ data: { path: file_path, success: false, replacements: 0 }
1263
1211
  };
1264
1212
  }
1265
- const content = await fs.readFile(file_path);
1266
- if (!content.includes(old_string)) {
1213
+ try {
1214
+ const exists = await fs.exists(file_path);
1215
+ if (!exists) {
1216
+ return {
1217
+ toolResponse: `Error: File "${file_path}" does not exist.`,
1218
+ data: { path: file_path, success: false, replacements: 0 }
1219
+ };
1220
+ }
1221
+ const content = await fs.readFile(file_path);
1222
+ if (!content.includes(old_string)) {
1223
+ return {
1224
+ toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
1225
+ data: { path: file_path, success: false, replacements: 0 }
1226
+ };
1227
+ }
1228
+ const escapedOldString = escapeRegExp(old_string);
1229
+ const globalRegex = new RegExp(escapedOldString, "g");
1230
+ const occurrences = (content.match(globalRegex) || []).length;
1231
+ if (!replace_all && occurrences > 1) {
1232
+ return {
1233
+ toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
1234
+ data: { path: file_path, success: false, replacements: 0 }
1235
+ };
1236
+ }
1237
+ let newContent;
1238
+ let replacements;
1239
+ if (replace_all) {
1240
+ newContent = content.split(old_string).join(new_string);
1241
+ replacements = occurrences;
1242
+ } else {
1243
+ const index = content.indexOf(old_string);
1244
+ newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
1245
+ replacements = 1;
1246
+ }
1247
+ await fs.writeFile(file_path, newContent);
1248
+ const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
1267
1249
  return {
1268
- toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
1269
- data: {
1270
- path: file_path,
1271
- success: false,
1272
- replacements: 0
1273
- }
1250
+ toolResponse: `${summary} in ${file_path}`,
1251
+ data: { path: file_path, success: true, replacements }
1274
1252
  };
1275
- }
1276
- const escapedOldString = escapeRegExp(old_string);
1277
- const globalRegex = new RegExp(escapedOldString, "g");
1278
- const occurrences = (content.match(globalRegex) || []).length;
1279
- if (!replace_all && occurrences > 1) {
1253
+ } catch (error) {
1254
+ const message = error instanceof Error ? error.message : "Unknown error";
1280
1255
  return {
1281
- toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
1282
- data: {
1283
- path: file_path,
1284
- success: false,
1285
- replacements: 0
1286
- }
1256
+ toolResponse: `Error editing file "${file_path}": ${message}`,
1257
+ data: { path: file_path, success: false, replacements: 0 }
1287
1258
  };
1288
1259
  }
1289
- let newContent;
1290
- let replacements;
1291
- if (replace_all) {
1292
- newContent = content.split(old_string).join(new_string);
1293
- replacements = occurrences;
1294
- } else {
1295
- const index = content.indexOf(old_string);
1296
- newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
1297
- replacements = 1;
1298
- }
1299
- await fs.writeFile(file_path, newContent);
1300
- const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
1301
- return {
1302
- toolResponse: `${summary} in ${file_path}`,
1303
- data: {
1304
- path: file_path,
1305
- success: true,
1306
- replacements
1307
- }
1308
- };
1309
- } catch (error) {
1310
- const message = error instanceof Error ? error.message : "Unknown error";
1311
- return {
1312
- toolResponse: `Error editing file "${file_path}": ${message}`,
1313
- data: {
1314
- path: file_path,
1315
- success: false,
1316
- replacements: 0
1317
- }
1318
- };
1319
- }
1260
+ };
1320
1261
  }
1321
- var handleBashTool = (bashOptions) => async (args, _context) => {
1262
+ var createBashHandler = (bashOptions) => async (args, _context) => {
1322
1263
  const { command } = args;
1323
1264
  const mergedOptions = {
1324
1265
  ...bashOptions,
@@ -1420,6 +1361,11 @@ exports.ZeitlichPlugin = ZeitlichPlugin;
1420
1361
  exports.askUserQuestionTool = askUserQuestionTool;
1421
1362
  exports.bashTool = bashTool;
1422
1363
  exports.createAgentStateManager = createAgentStateManager;
1364
+ exports.createAskUserQuestionHandler = createAskUserQuestionHandler;
1365
+ exports.createBashHandler = createBashHandler;
1366
+ exports.createBashToolDescription = createBashToolDescription;
1367
+ exports.createEditHandler = createEditHandler;
1368
+ exports.createGlobHandler = createGlobHandler;
1423
1369
  exports.createSession = createSession;
1424
1370
  exports.createSharedActivities = createSharedActivities;
1425
1371
  exports.createTaskCreateHandler = createTaskCreateHandler;
@@ -1431,23 +1377,21 @@ exports.createThreadManager = createThreadManager;
1431
1377
  exports.createToolRouter = createToolRouter;
1432
1378
  exports.defineSubagent = defineSubagent;
1433
1379
  exports.defineTool = defineTool;
1434
- exports.editHandler = editHandler;
1435
1380
  exports.editTool = editTool;
1436
1381
  exports.getStateQuery = getStateQuery;
1437
- exports.globHandler = globHandler;
1438
1382
  exports.globTool = globTool;
1439
1383
  exports.grepTool = grepTool;
1440
- exports.handleAskUserQuestionToolResult = handleAskUserQuestionToolResult;
1441
- exports.handleBashTool = handleBashTool;
1442
1384
  exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
1443
1385
  exports.invokeModel = invokeModel;
1444
1386
  exports.isTerminalStatus = isTerminalStatus;
1387
+ exports.proxyDefaultThreadOps = proxyDefaultThreadOps;
1445
1388
  exports.readTool = readTool;
1446
1389
  exports.taskCreateTool = taskCreateTool;
1447
1390
  exports.taskGetTool = taskGetTool;
1448
1391
  exports.taskListTool = taskListTool;
1449
1392
  exports.taskUpdateTool = taskUpdateTool;
1450
1393
  exports.toTree = toTree;
1394
+ exports.withAutoAppend = withAutoAppend;
1451
1395
  exports.writeTool = writeTool;
1452
1396
  //# sourceMappingURL=index.cjs.map
1453
1397
  //# sourceMappingURL=index.cjs.map