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.js CHANGED
@@ -1,7 +1,7 @@
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 { mapStoredMessageToChatMessage, 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';
@@ -39,10 +39,10 @@ function createTaskTool(subagents) {
39
39
  return {
40
40
  name: TASK_TOOL,
41
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")
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
  }
@@ -77,107 +77,15 @@ function createTaskHandler(subagents) {
77
77
  };
78
78
  };
79
79
  }
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
80
 
162
81
  // src/lib/tool-router.ts
163
- var buildIntoolDefinitions = {
164
- [bashTool.name]: bashTool,
165
- [taskCreateTool.name]: taskCreateTool
166
- };
167
82
  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
- });
83
+ const { appendToolResult } = options;
177
84
  const toolMap = /* @__PURE__ */ new Map();
178
85
  for (const [_key, tool] of Object.entries(options.tools)) {
179
86
  toolMap.set(tool.name, tool);
180
87
  }
88
+ const isEnabled = (tool) => tool.enabled !== false;
181
89
  if (options.subagents) {
182
90
  const subagentHooksMap = /* @__PURE__ */ new Map();
183
91
  for (const s of options.subagents) {
@@ -205,24 +113,6 @@ function createToolRouter(options) {
205
113
  }
206
114
  });
207
115
  }
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
116
  async function processToolCall(toolCall, turn, handlerContext) {
227
117
  const startTime = Date.now();
228
118
  const tool = toolMap.get(toolCall.name);
@@ -238,6 +128,7 @@ function createToolRouter(options) {
238
128
  await appendToolResult({
239
129
  threadId: options.threadId,
240
130
  toolCallId: toolCall.id,
131
+ toolName: toolCall.name,
241
132
  content: JSON.stringify({
242
133
  skipped: true,
243
134
  reason: "Skipped by PreToolUse hook"
@@ -259,6 +150,7 @@ function createToolRouter(options) {
259
150
  await appendToolResult({
260
151
  threadId: options.threadId,
261
152
  toolCallId: toolCall.id,
153
+ toolName: toolCall.name,
262
154
  content: JSON.stringify({
263
155
  skipped: true,
264
156
  reason: "Skipped by tool PreToolUse hook"
@@ -272,14 +164,22 @@ function createToolRouter(options) {
272
164
  }
273
165
  let result;
274
166
  let content;
167
+ let resultAppended = false;
275
168
  try {
276
169
  if (tool) {
170
+ const enrichedContext = {
171
+ ...handlerContext ?? {},
172
+ threadId: options.threadId,
173
+ toolCallId: toolCall.id,
174
+ toolName: toolCall.name
175
+ };
277
176
  const response = await tool.handler(
278
177
  effectiveArgs,
279
- handlerContext ?? {}
178
+ enrichedContext
280
179
  );
281
180
  result = response.data;
282
181
  content = response.toolResponse;
182
+ resultAppended = response.resultAppended === true;
283
183
  } else {
284
184
  result = { error: `Unknown tool: ${toolCall.name}` };
285
185
  content = JSON.stringify(result, null, 2);
@@ -325,11 +225,14 @@ function createToolRouter(options) {
325
225
  throw error;
326
226
  }
327
227
  }
328
- await appendToolResult({
329
- threadId: options.threadId,
330
- toolCallId: toolCall.id,
331
- content
332
- });
228
+ if (!resultAppended) {
229
+ await appendToolResult({
230
+ threadId: options.threadId,
231
+ toolCallId: toolCall.id,
232
+ toolName: toolCall.name,
233
+ content
234
+ });
235
+ }
333
236
  const toolResult = {
334
237
  toolCallId: toolCall.id,
335
238
  name: toolCall.name,
@@ -359,11 +262,11 @@ function createToolRouter(options) {
359
262
  return {
360
263
  // --- Methods from registry ---
361
264
  hasTools() {
362
- return toolMap.size > 0;
265
+ return Array.from(toolMap.values()).some(isEnabled);
363
266
  },
364
267
  parseToolCall(toolCall) {
365
268
  const tool = toolMap.get(toolCall.name);
366
- if (!tool) {
269
+ if (!tool || !isEnabled(tool)) {
367
270
  throw new Error(`Tool ${toolCall.name} not found`);
368
271
  }
369
272
  const parsedArgs = tool.schema.parse(toolCall.args);
@@ -374,13 +277,14 @@ function createToolRouter(options) {
374
277
  };
375
278
  },
376
279
  hasTool(name) {
377
- return toolMap.has(name);
280
+ const tool = toolMap.get(name);
281
+ return tool !== void 0 && isEnabled(tool);
378
282
  },
379
283
  getToolNames() {
380
- return Array.from(toolMap.keys());
284
+ return Array.from(toolMap.entries()).filter(([, tool]) => isEnabled(tool)).map(([name]) => name);
381
285
  },
382
286
  getToolDefinitions() {
383
- return Array.from(toolMap).map(([name, tool]) => ({
287
+ return Array.from(toolMap).filter(([, tool]) => isEnabled(tool)).map(([name, tool]) => ({
384
288
  name,
385
289
  description: tool.description,
386
290
  schema: tool.schema,
@@ -419,19 +323,28 @@ function createToolRouter(options) {
419
323
  }
420
324
  const handlerContext = context?.handlerContext ?? {};
421
325
  const processOne = async (toolCall) => {
326
+ const enrichedContext = {
327
+ ...handlerContext ?? {},
328
+ threadId: options.threadId,
329
+ toolCallId: toolCall.id,
330
+ toolName: toolCall.name
331
+ };
422
332
  const response = await handler(
423
333
  toolCall.args,
424
- handlerContext
334
+ enrichedContext
425
335
  );
426
- await appendToolResult({
427
- threadId: options.threadId,
428
- toolCallId: toolCall.id,
429
- content: response.toolResponse
430
- });
336
+ if (!response.resultAppended) {
337
+ await appendToolResult({
338
+ threadId: options.threadId,
339
+ toolCallId: toolCall.id,
340
+ toolName: toolCall.name,
341
+ content: response.toolResponse
342
+ });
343
+ }
431
344
  return {
432
345
  toolCallId: toolCall.id,
433
346
  name: toolCall.name,
434
- data: response.data ?? null
347
+ data: response.data
435
348
  };
436
349
  };
437
350
  if (options.parallel) {
@@ -457,6 +370,21 @@ function createToolRouter(options) {
457
370
  }
458
371
  };
459
372
  }
373
+ function withAutoAppend(threadHandler, handler) {
374
+ return async (args, context) => {
375
+ const response = await handler(args, context);
376
+ const threadId = context.threadId;
377
+ const toolCallId = context.toolCallId;
378
+ const toolName = context.toolName;
379
+ await threadHandler({
380
+ threadId,
381
+ toolCallId,
382
+ toolName,
383
+ content: response.toolResponse
384
+ });
385
+ return { toolResponse: "", data: response.data, resultAppended: true };
386
+ };
387
+ }
460
388
  function defineTool(tool) {
461
389
  return tool;
462
390
  }
@@ -468,51 +396,24 @@ function hasNoOtherToolCalls(toolCalls, excludeName) {
468
396
  }
469
397
 
470
398
  // src/lib/session.ts
471
- async function resolvePrompt(prompt) {
472
- if (typeof prompt === "function") {
473
- return prompt();
474
- }
475
- return prompt;
476
- }
477
399
  var createSession = async ({
478
400
  threadId,
479
401
  agentName,
480
402
  maxTurns = 50,
481
403
  metadata = {},
482
404
  runAgent,
483
- baseSystemPrompt,
484
- instructionsPrompt,
405
+ threadOps,
485
406
  buildContextMessage,
486
- buildFileTree = async () => "",
487
407
  subagents,
488
408
  tools = {},
489
409
  processToolsInParallel = true,
490
- buildInTools = {},
491
410
  hooks = {}
492
411
  }) => {
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
412
  const toolRouter = createToolRouter({
511
413
  tools,
414
+ appendToolResult: threadOps.appendToolResult,
512
415
  threadId,
513
416
  hooks,
514
- buildInTools,
515
- fileTree,
516
417
  subagents,
517
418
  parallel: processToolsInParallel
518
419
  });
@@ -537,15 +438,8 @@ var createSession = async ({
537
438
  });
538
439
  }
539
440
  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());
441
+ await threadOps.initializeThread(threadId);
442
+ await threadOps.appendHumanMessage(threadId, await buildContextMessage());
549
443
  let exitReason = "completed";
550
444
  try {
551
445
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
@@ -566,54 +460,25 @@ var createSession = async ({
566
460
  exitReason = "completed";
567
461
  return message;
568
462
  }
569
- const rawToolCalls = await parseToolCalls(message);
463
+ const rawToolCalls = await threadOps.parseToolCalls(message);
570
464
  const parsedToolCalls = [];
571
- for (const tc of rawToolCalls.filter(
572
- (tc2) => tc2.name !== "Task"
573
- )) {
465
+ for (const tc of rawToolCalls) {
574
466
  try {
575
467
  parsedToolCalls.push(toolRouter.parseToolCall(tc));
576
468
  } catch (error) {
577
- await appendToolResult({
469
+ await threadOps.appendToolResult({
578
470
  threadId,
579
471
  toolCallId: tc.id ?? "",
472
+ toolName: tc.name,
580
473
  content: JSON.stringify({
581
474
  error: `Invalid tool call for "${tc.name}": ${error instanceof Error ? error.message : String(error)}`
582
475
  })
583
476
  });
584
477
  }
585
478
  }
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
- );
479
+ await toolRouter.processToolCalls(parsedToolCalls, {
480
+ turn: currentTurn
481
+ });
617
482
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
618
483
  exitReason = "waiting_for_input";
619
484
  break;
@@ -632,6 +497,26 @@ var createSession = async ({
632
497
  }
633
498
  };
634
499
  };
500
+ function proxyDefaultThreadOps(options) {
501
+ const activities = proxyActivities(
502
+ options ?? {
503
+ startToCloseTimeout: "30m",
504
+ retry: {
505
+ maximumAttempts: 6,
506
+ initialInterval: "5s",
507
+ maximumInterval: "15m",
508
+ backoffCoefficient: 4
509
+ },
510
+ heartbeatTimeout: "5m"
511
+ }
512
+ );
513
+ return {
514
+ initializeThread: activities.initializeThread,
515
+ appendHumanMessage: activities.appendHumanMessage,
516
+ appendToolResult: activities.appendToolResult,
517
+ parseToolCalls: activities.parseToolCalls
518
+ };
519
+ }
635
520
 
636
521
  // src/lib/types.ts
637
522
  function isTerminalStatus(status) {
@@ -768,18 +653,18 @@ Usage notes:
768
653
  * Use multiSelect: true to allow multiple answers to be selected for a question
769
654
  * If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
770
655
  `,
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()
656
+ schema: z3.object({
657
+ questions: z3.array(
658
+ z3.object({
659
+ question: z3.string().describe("The full question text to display"),
660
+ header: z3.string().describe("Short label for the question (max 12 characters)"),
661
+ options: z3.array(
662
+ z3.object({
663
+ label: z3.string(),
664
+ description: z3.string()
780
665
  })
781
666
  ).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")
667
+ multiSelect: z3.boolean().describe("If true, users can select multiple options")
783
668
  })
784
669
  )
785
670
  }),
@@ -857,7 +742,7 @@ var writeTool = {
857
742
  description: `Create or overwrite a file with new content.
858
743
 
859
744
  Usage:
860
- - Provide the absolute virtual path to the file
745
+ - Provide the absolute path to the file
861
746
  - The file will be created if it doesn't exist
862
747
  - If the file exists, it will be completely overwritten
863
748
 
@@ -867,7 +752,7 @@ IMPORTANT:
867
752
  - Path must be absolute (e.g., "/docs/readme.md", not "docs/readme.md")
868
753
  `,
869
754
  schema: z.object({
870
- file_path: z.string().describe("The absolute virtual path to the file to write"),
755
+ file_path: z.string().describe("The absolute path to the file to write"),
871
756
  content: z.string().describe("The content to write to the file")
872
757
  }),
873
758
  strict: true
@@ -900,6 +785,60 @@ IMPORTANT:
900
785
  }),
901
786
  strict: true
902
787
  };
788
+ var taskCreateTool = {
789
+ name: "TaskCreate",
790
+ 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.
791
+ It also helps the user understand the progress of the task and overall progress of their requests.
792
+
793
+ ## When to Use This Tool
794
+
795
+ Use this tool proactively in these scenarios:
796
+
797
+ - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
798
+ - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
799
+ - User explicitly requests todo list - When the user directly asks you to use the todo list
800
+ - User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
801
+ - After receiving new instructions - Immediately capture user requirements as tasks
802
+ - When you start working on a task - Mark it as in_progress BEFORE beginning work
803
+ - After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
804
+
805
+ ## When NOT to Use This Tool
806
+
807
+ Skip using this tool when:
808
+ - There is only a single, straightforward task
809
+ - The task is trivial and tracking it provides no organizational benefit
810
+ - The task can be completed in less than 3 trivial steps
811
+ - The task is purely conversational or informational
812
+
813
+ 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.
814
+
815
+ ## Task Fields
816
+
817
+ - **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
818
+ - **description**: Detailed description of what needs to be done, including context and acceptance criteria
819
+ - **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.
820
+
821
+ **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\`.
822
+
823
+ ## Tips
824
+
825
+ - Create tasks with clear, specific subjects that describe the outcome
826
+ - Include enough detail in the description for another agent to understand and complete the task
827
+ - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
828
+ - Check TaskList first to avoid creating duplicate tasks`,
829
+ schema: z3.object({
830
+ subject: z3.string().describe(
831
+ 'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
832
+ ),
833
+ description: z3.string().describe(
834
+ "Detailed description of what needs to be done, including context and acceptance criteria"
835
+ ),
836
+ activeForm: z3.string().describe(
837
+ '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.'
838
+ ),
839
+ metadata: z3.record(z3.string(), z3.string()).describe("Arbitrary key-value pairs for tracking")
840
+ })
841
+ };
903
842
  function createTaskCreateHandler(stateManager) {
904
843
  return (args) => {
905
844
  const task = {
@@ -922,8 +861,8 @@ function createTaskCreateHandler(stateManager) {
922
861
  var taskGetTool = {
923
862
  name: "TaskGet",
924
863
  description: `Retrieve full task details including dependencies.`,
925
- schema: z5.object({
926
- taskId: z5.string().describe("The ID of the task to get")
864
+ schema: z3.object({
865
+ taskId: z3.string().describe("The ID of the task to get")
927
866
  })
928
867
  };
929
868
 
@@ -946,12 +885,12 @@ function createTaskGetHandler(stateManager) {
946
885
  var taskListTool = {
947
886
  name: "TaskList",
948
887
  description: `List all tasks with current state.`,
949
- schema: z5.object({})
888
+ schema: z3.object({})
950
889
  };
951
890
 
952
891
  // src/tools/task-list/handler.ts
953
892
  function createTaskListHandler(stateManager) {
954
- return (_args) => {
893
+ return () => {
955
894
  const taskList = stateManager.getTasks();
956
895
  return {
957
896
  toolResponse: JSON.stringify(taskList, null, 2),
@@ -962,11 +901,11 @@ function createTaskListHandler(stateManager) {
962
901
  var taskUpdateTool = {
963
902
  name: "TaskUpdate",
964
903
  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")
904
+ schema: z3.object({
905
+ taskId: z3.string().describe("The ID of the task to get"),
906
+ status: z3.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
907
+ addBlockedBy: z3.array(z3.string()).describe("The IDs of the tasks that are blocking this task"),
908
+ addBlocks: z3.array(z3.string()).describe("The IDs of the tasks that this task is blocking")
970
909
  })
971
910
  };
972
911
 
@@ -1014,6 +953,33 @@ function createTaskUpdateHandler(stateManager) {
1014
953
  };
1015
954
  };
1016
955
  }
956
+ var createBashToolDescription = ({
957
+ fileTree
958
+ }) => `Execute shell commands in a 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
+ Current file tree:
966
+ ${fileTree}`;
967
+ var bashTool = {
968
+ name: "Bash",
969
+ description: `Execute shell commands in a sandboxed bash environment.
970
+
971
+ Use this tool to:
972
+ - Run shell commands (ls, cat, grep, find, etc.)
973
+ - Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
974
+ - Inspect files and directories
975
+ `,
976
+ schema: z3.object({
977
+ command: z3.string().describe(
978
+ "The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
979
+ )
980
+ }),
981
+ strict: true
982
+ };
1017
983
 
1018
984
  // node_modules/uuid/dist/esm-node/stringify.js
1019
985
  var byteToHex = [];
@@ -1062,25 +1028,33 @@ function getThreadKey(threadId, key) {
1062
1028
  return `thread:${threadId}:${key}`;
1063
1029
  }
1064
1030
  function createThreadManager(config) {
1065
- const { redis, threadId, key = "messages" } = config;
1031
+ const {
1032
+ redis,
1033
+ threadId,
1034
+ key = "messages",
1035
+ serialize = (m) => JSON.stringify(m),
1036
+ deserialize = (raw) => JSON.parse(raw)
1037
+ } = config;
1066
1038
  const redisKey = getThreadKey(threadId, key);
1067
- return {
1039
+ const base = {
1068
1040
  async initialize() {
1069
1041
  await redis.del(redisKey);
1070
1042
  },
1071
1043
  async load() {
1072
1044
  const data = await redis.lrange(redisKey, 0, -1);
1073
- return data.map((item) => JSON.parse(item));
1045
+ return data.map(deserialize);
1074
1046
  },
1075
1047
  async append(messages) {
1076
1048
  if (messages.length > 0) {
1077
- await redis.rpush(redisKey, ...messages.map((m) => JSON.stringify(m)));
1049
+ await redis.rpush(redisKey, ...messages.map(serialize));
1078
1050
  await redis.expire(redisKey, THREAD_TTL_SECONDS);
1079
1051
  }
1080
1052
  },
1081
1053
  async delete() {
1082
1054
  await redis.del(redisKey);
1083
- },
1055
+ }
1056
+ };
1057
+ const helpers = {
1084
1058
  createHumanMessage(content) {
1085
1059
  return new HumanMessage({
1086
1060
  id: v4_default(),
@@ -1100,40 +1074,27 @@ function createThreadManager(config) {
1100
1074
  },
1101
1075
  createToolMessage(content, toolCallId) {
1102
1076
  return new ToolMessage({
1103
- // Cast needed due to langchain type compatibility
1104
1077
  content,
1105
1078
  tool_call_id: toolCallId
1106
1079
  }).toDict();
1107
1080
  },
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
1081
  async appendHumanMessage(content) {
1118
- const message = this.createHumanMessage(content);
1119
- await this.append([message]);
1082
+ const message = helpers.createHumanMessage(content);
1083
+ await base.append([message]);
1120
1084
  },
1121
1085
  async appendToolMessage(content, toolCallId) {
1122
- const message = this.createToolMessage(content, toolCallId);
1123
- await this.append([message]);
1086
+ const message = helpers.createToolMessage(content, toolCallId);
1087
+ await base.append([message]);
1124
1088
  },
1125
1089
  async appendAIMessage(content) {
1126
- const message = this.createAIMessage(content);
1127
- await this.append([message]);
1090
+ const message = helpers.createAIMessage(content);
1091
+ await base.append([message]);
1128
1092
  }
1129
1093
  };
1094
+ return Object.assign(base, helpers);
1130
1095
  }
1131
1096
  function createSharedActivities(redis) {
1132
1097
  return {
1133
- async appendSystemMessage(threadId, content) {
1134
- const thread = createThreadManager({ redis, threadId });
1135
- await thread.appendSystemMessage(content);
1136
- },
1137
1098
  async appendToolResult(config) {
1138
1099
  const { threadId, toolCallId, content } = config;
1139
1100
  const thread = createThreadManager({ redis, threadId });
@@ -1206,7 +1167,7 @@ async function invokeModel({
1206
1167
  }
1207
1168
  };
1208
1169
  }
1209
- var handleAskUserQuestionToolResult = async (args) => {
1170
+ var createAskUserQuestionHandler = () => async (args) => {
1210
1171
  const messages = args.questions.map(
1211
1172
  ({ question, header, options, multiSelect }) => new AIMessage({
1212
1173
  content: question,
@@ -1219,99 +1180,79 @@ var handleAskUserQuestionToolResult = async (args) => {
1219
1180
  );
1220
1181
  return { toolResponse: "Question submitted", data: { chatMessages: messages } };
1221
1182
  };
1222
- async function globHandler(_args, fs) {
1223
- new Bash({ fs });
1224
- return Promise.resolve({
1225
- toolResponse: "Hello, world!",
1226
- data: { files: [] }
1227
- });
1183
+ function createGlobHandler(fs) {
1184
+ return async (_args) => {
1185
+ new Bash({ fs });
1186
+ return {
1187
+ toolResponse: "Hello, world!",
1188
+ data: { files: [] }
1189
+ };
1190
+ };
1228
1191
  }
1229
1192
 
1230
1193
  // src/tools/edit/handler.ts
1231
1194
  function escapeRegExp(str) {
1232
1195
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1233
1196
  }
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) {
1197
+ function createEditHandler(fs) {
1198
+ return async (args) => {
1199
+ const { file_path, old_string, new_string, replace_all = false } = args;
1200
+ if (old_string === new_string) {
1249
1201
  return {
1250
- toolResponse: `Error: File "${file_path}" does not exist.`,
1251
- data: {
1252
- path: file_path,
1253
- success: false,
1254
- replacements: 0
1255
- }
1202
+ toolResponse: `Error: old_string and new_string must be different.`,
1203
+ data: { path: file_path, success: false, replacements: 0 }
1256
1204
  };
1257
1205
  }
1258
- const content = await fs.readFile(file_path);
1259
- if (!content.includes(old_string)) {
1206
+ try {
1207
+ const exists = await fs.exists(file_path);
1208
+ if (!exists) {
1209
+ return {
1210
+ toolResponse: `Error: File "${file_path}" does not exist.`,
1211
+ data: { path: file_path, success: false, replacements: 0 }
1212
+ };
1213
+ }
1214
+ const content = await fs.readFile(file_path);
1215
+ if (!content.includes(old_string)) {
1216
+ return {
1217
+ toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
1218
+ data: { path: file_path, success: false, replacements: 0 }
1219
+ };
1220
+ }
1221
+ const escapedOldString = escapeRegExp(old_string);
1222
+ const globalRegex = new RegExp(escapedOldString, "g");
1223
+ const occurrences = (content.match(globalRegex) || []).length;
1224
+ if (!replace_all && occurrences > 1) {
1225
+ return {
1226
+ toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
1227
+ data: { path: file_path, success: false, replacements: 0 }
1228
+ };
1229
+ }
1230
+ let newContent;
1231
+ let replacements;
1232
+ if (replace_all) {
1233
+ newContent = content.split(old_string).join(new_string);
1234
+ replacements = occurrences;
1235
+ } else {
1236
+ const index = content.indexOf(old_string);
1237
+ newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
1238
+ replacements = 1;
1239
+ }
1240
+ await fs.writeFile(file_path, newContent);
1241
+ const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
1260
1242
  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
- }
1243
+ toolResponse: `${summary} in ${file_path}`,
1244
+ data: { path: file_path, success: true, replacements }
1267
1245
  };
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) {
1246
+ } catch (error) {
1247
+ const message = error instanceof Error ? error.message : "Unknown error";
1273
1248
  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
- }
1249
+ toolResponse: `Error editing file "${file_path}": ${message}`,
1250
+ data: { path: file_path, success: false, replacements: 0 }
1280
1251
  };
1281
1252
  }
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
- }
1253
+ };
1313
1254
  }
1314
- var handleBashTool = (bashOptions) => async (args, _context) => {
1255
+ var createBashHandler = (bashOptions) => async (args, _context) => {
1315
1256
  const { command } = args;
1316
1257
  const mergedOptions = {
1317
1258
  ...bashOptions,
@@ -1408,6 +1349,6 @@ var toTree = async (fs, opts = {}) => {
1408
1349
  return base + subtree;
1409
1350
  };
1410
1351
 
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 };
1352
+ export { AGENT_HANDLER_NAMES, ZeitlichPlugin, askUserQuestionTool, bashTool, createAgentStateManager, createAskUserQuestionHandler, createBashHandler, createBashToolDescription, createEditHandler, createGlobHandler, createSession, createSharedActivities, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskTool, createTaskUpdateHandler, createThreadManager, createToolRouter, defineSubagent, defineTool, editTool, getStateQuery, globTool, grepTool, hasNoOtherToolCalls, invokeModel, isTerminalStatus, proxyDefaultThreadOps, readTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, writeTool };
1412
1353
  //# sourceMappingURL=index.js.map
1413
1354
  //# sourceMappingURL=index.js.map