zeitlich 0.1.1 → 0.2.0

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 (58) hide show
  1. package/README.md +165 -180
  2. package/dist/index.cjs +1314 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +128 -0
  5. package/dist/index.d.ts +51 -75
  6. package/dist/index.js +741 -1091
  7. package/dist/index.js.map +1 -1
  8. package/dist/workflow-uVNF7zoe.d.cts +941 -0
  9. package/dist/workflow-uVNF7zoe.d.ts +941 -0
  10. package/dist/workflow.cjs +914 -0
  11. package/dist/workflow.cjs.map +1 -0
  12. package/dist/workflow.d.cts +5 -0
  13. package/dist/workflow.d.ts +2 -1
  14. package/dist/workflow.js +543 -423
  15. package/dist/workflow.js.map +1 -1
  16. package/package.json +19 -17
  17. package/src/activities.ts +112 -0
  18. package/src/index.ts +49 -0
  19. package/src/lib/fs.ts +80 -0
  20. package/src/lib/model-invoker.ts +75 -0
  21. package/src/lib/session.ts +216 -0
  22. package/src/lib/state-manager.ts +268 -0
  23. package/src/lib/thread-manager.ts +169 -0
  24. package/src/lib/tool-router.ts +717 -0
  25. package/src/lib/types.ts +354 -0
  26. package/src/plugin.ts +28 -0
  27. package/src/tools/ask-user-question/handler.ts +25 -0
  28. package/src/tools/ask-user-question/tool.ts +46 -0
  29. package/src/tools/bash/bash.test.ts +104 -0
  30. package/src/tools/bash/handler.ts +36 -0
  31. package/src/tools/bash/tool.ts +20 -0
  32. package/src/tools/edit/handler.ts +156 -0
  33. package/src/tools/edit/tool.ts +39 -0
  34. package/src/tools/glob/handler.ts +62 -0
  35. package/src/tools/glob/tool.ts +27 -0
  36. package/src/tools/grep/tool.ts +45 -0
  37. package/src/tools/read/tool.ts +33 -0
  38. package/src/tools/task/handler.ts +75 -0
  39. package/src/tools/task/tool.ts +96 -0
  40. package/src/tools/task-create/handler.ts +49 -0
  41. package/src/tools/task-create/tool.ts +66 -0
  42. package/src/tools/task-get/handler.ts +38 -0
  43. package/src/tools/task-get/tool.ts +11 -0
  44. package/src/tools/task-list/handler.ts +33 -0
  45. package/src/tools/task-list/tool.ts +9 -0
  46. package/src/tools/task-update/handler.ts +79 -0
  47. package/src/tools/task-update/tool.ts +20 -0
  48. package/src/tools/write/tool.ts +26 -0
  49. package/src/workflow.ts +138 -0
  50. package/tsup.config.ts +20 -0
  51. package/dist/index.d.mts +0 -152
  52. package/dist/index.mjs +0 -1587
  53. package/dist/index.mjs.map +0 -1
  54. package/dist/workflow-7_MT-5-w.d.mts +0 -1203
  55. package/dist/workflow-7_MT-5-w.d.ts +0 -1203
  56. package/dist/workflow.d.mts +0 -4
  57. package/dist/workflow.mjs +0 -739
  58. package/dist/workflow.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,16 +1,10 @@
1
- 'use strict';
2
-
3
- var workflow = require('@temporalio/workflow');
4
- var z2 = require('zod');
5
- var minimatch = require('minimatch');
6
- var plugin = require('@temporalio/plugin');
7
- var messages = require('@langchain/core/messages');
8
- var crypto = require('crypto');
9
-
10
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
-
12
- var z2__default = /*#__PURE__*/_interopDefault(z2);
13
- var crypto__default = /*#__PURE__*/_interopDefault(crypto);
1
+ import { defineQuery, proxyActivities, setHandler, workflowInfo, uuid4, executeChild } from '@temporalio/workflow';
2
+ import z4, { z } from 'zod';
3
+ import { SimplePlugin } from '@temporalio/plugin';
4
+ import { mapStoredMessageToChatMessage, mapStoredMessagesToChatMessages, AIMessage, SystemMessage, ToolMessage, HumanMessage } from '@langchain/core/messages';
5
+ import crypto from 'crypto';
6
+ import { Context } from '@temporalio/activity';
7
+ import { Bash } from 'just-bash';
14
8
 
15
9
  // src/lib/session.ts
16
10
  var TASK_TOOL = "Task";
@@ -45,24 +39,24 @@ function createTaskTool(subagents) {
45
39
  return {
46
40
  name: TASK_TOOL,
47
41
  description: buildTaskDescription(subagents),
48
- schema: z2__default.default.object({
49
- subagent: z2__default.default.enum(names).describe("The type of subagent to launch"),
50
- description: z2__default.default.string().describe("A short (3-5 word) description of the task"),
51
- prompt: z2__default.default.string().describe("The task for the agent to perform")
42
+ schema: z4.object({
43
+ subagent: z4.enum(names).describe("The type of subagent to launch"),
44
+ description: z4.string().describe("A short (3-5 word) description of the task"),
45
+ prompt: z4.string().describe("The task for the agent to perform")
52
46
  })
53
47
  };
54
48
  }
55
49
  function createTaskHandler(subagents) {
56
- const { workflowId: parentWorkflowId, taskQueue: parentTaskQueue } = workflow.workflowInfo();
57
- return async (args, _toolCallId) => {
50
+ const { workflowId: parentWorkflowId, taskQueue: parentTaskQueue } = workflowInfo();
51
+ return async (args) => {
58
52
  const config = subagents.find((s) => s.name === args.subagent);
59
53
  if (!config) {
60
54
  throw new Error(
61
55
  `Unknown subagent: ${args.subagent}. Available: ${subagents.map((s) => s.name).join(", ")}`
62
56
  );
63
57
  }
64
- const childWorkflowId = `${parentWorkflowId}-${args.subagent}-${workflow.uuid4()}`;
65
- const childResult = await workflow.executeChild(config.workflowType, {
58
+ const childWorkflowId = `${parentWorkflowId}-${args.subagent}-${uuid4()}`;
59
+ const childResult = await executeChild(config.workflowType, {
66
60
  workflowId: childWorkflowId,
67
61
  args: [{ prompt: args.prompt }],
68
62
  taskQueue: config.taskQueue ?? parentTaskQueue
@@ -78,35 +72,332 @@ function createTaskHandler(subagents) {
78
72
  };
79
73
  };
80
74
  }
75
+ var createBashToolDescription = ({
76
+ fileTree
77
+ }) => `tool to execute bash commands, the file tree is: ${fileTree}`;
78
+ var bashTool = {
79
+ name: "Bash",
80
+ description: "tool to execute bash commands",
81
+ schema: z4.object({
82
+ command: z4.string().describe("stringified command to be executed inside the Bash")
83
+ }),
84
+ strict: true
85
+ };
86
+ var taskCreateTool = {
87
+ name: "TaskCreate",
88
+ 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.
89
+ It also helps the user understand the progress of the task and overall progress of their requests.
90
+
91
+ ## When to Use This Tool
92
+
93
+ Use this tool proactively in these scenarios:
94
+
95
+ - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
96
+ - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
97
+ - User explicitly requests todo list - When the user directly asks you to use the todo list
98
+ - User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
99
+ - After receiving new instructions - Immediately capture user requirements as tasks
100
+ - When you start working on a task - Mark it as in_progress BEFORE beginning work
101
+ - After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
102
+
103
+ ## When NOT to Use This Tool
104
+
105
+ Skip using this tool when:
106
+ - There is only a single, straightforward task
107
+ - The task is trivial and tracking it provides no organizational benefit
108
+ - The task can be completed in less than 3 trivial steps
109
+ - The task is purely conversational or informational
110
+
111
+ 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.
112
+
113
+ ## Task Fields
114
+
115
+ - **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
116
+ - **description**: Detailed description of what needs to be done, including context and acceptance criteria
117
+ - **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.
118
+
119
+ **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\`.
120
+
121
+ ## Tips
122
+
123
+ - Create tasks with clear, specific subjects that describe the outcome
124
+ - Include enough detail in the description for another agent to understand and complete the task
125
+ - After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
126
+ - Check TaskList first to avoid creating duplicate tasks`,
127
+ schema: z4.object({
128
+ subject: z4.string().describe(
129
+ 'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
130
+ ),
131
+ description: z4.string().describe(
132
+ "Detailed description of what needs to be done, including context and acceptance criteria"
133
+ ),
134
+ activeForm: z4.string().describe(
135
+ '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.'
136
+ ),
137
+ metadata: z4.record(z4.string(), z4.string()).describe("Arbitrary key-value pairs for tracking")
138
+ })
139
+ };
81
140
 
82
- // src/lib/subagent-support.ts
83
- function withSubagentSupport(userTools, config) {
84
- if (config.subagents.length === 0) {
85
- throw new Error("withSubagentSupport requires at least one subagent");
141
+ // src/lib/tool-router.ts
142
+ var buildIntoolDefinitions = {
143
+ [bashTool.name]: bashTool,
144
+ [taskCreateTool.name]: taskCreateTool
145
+ };
146
+ function createToolRouter(options) {
147
+ const { appendToolResult } = proxyActivities({
148
+ startToCloseTimeout: "2m",
149
+ retry: {
150
+ maximumAttempts: 3,
151
+ initialInterval: "5s",
152
+ maximumInterval: "15m",
153
+ backoffCoefficient: 4
154
+ }
155
+ });
156
+ const toolMap = /* @__PURE__ */ new Map();
157
+ for (const [_key, tool] of Object.entries(options.tools)) {
158
+ toolMap.set(tool.name, tool);
159
+ }
160
+ if (options.subagents) {
161
+ toolMap.set("Task", {
162
+ ...createTaskTool(options.subagents),
163
+ handler: createTaskHandler(options.subagents)
164
+ });
165
+ }
166
+ if (options.buildInTools) {
167
+ for (const [key, value] of Object.entries(options.buildInTools)) {
168
+ if (key === bashTool.name) {
169
+ toolMap.set(key, {
170
+ ...buildIntoolDefinitions[key],
171
+ description: createBashToolDescription({
172
+ fileTree: options.fileTree
173
+ }),
174
+ handler: value
175
+ });
176
+ } else {
177
+ toolMap.set(key, {
178
+ ...buildIntoolDefinitions[key],
179
+ handler: value
180
+ });
181
+ }
182
+ }
183
+ }
184
+ async function processToolCall(toolCall, turn, handlerContext) {
185
+ const startTime = Date.now();
186
+ let effectiveArgs = toolCall.args;
187
+ if (options.hooks?.onPreToolUse) {
188
+ const preResult = await options.hooks.onPreToolUse({
189
+ toolCall,
190
+ threadId: options.threadId,
191
+ turn
192
+ });
193
+ if (preResult?.skip) {
194
+ await appendToolResult({
195
+ threadId: options.threadId,
196
+ toolCallId: toolCall.id,
197
+ content: JSON.stringify({
198
+ skipped: true,
199
+ reason: "Skipped by PreToolUse hook"
200
+ })
201
+ });
202
+ return null;
203
+ }
204
+ if (preResult?.modifiedArgs !== void 0) {
205
+ effectiveArgs = preResult.modifiedArgs;
206
+ }
207
+ }
208
+ const tool = toolMap.get(toolCall.name);
209
+ let result;
210
+ let content;
211
+ try {
212
+ if (tool) {
213
+ const response = await tool.handler(
214
+ effectiveArgs,
215
+ handlerContext ?? {}
216
+ );
217
+ result = response.result;
218
+ content = response.content;
219
+ } else {
220
+ result = { error: `Unknown tool: ${toolCall.name}` };
221
+ content = JSON.stringify(result, null, 2);
222
+ }
223
+ } catch (error) {
224
+ if (options.hooks?.onPostToolUseFailure) {
225
+ const failureResult = await options.hooks.onPostToolUseFailure({
226
+ toolCall,
227
+ error: error instanceof Error ? error : new Error(String(error)),
228
+ threadId: options.threadId,
229
+ turn
230
+ });
231
+ if (failureResult?.fallbackContent !== void 0) {
232
+ content = failureResult.fallbackContent;
233
+ result = { error: String(error), recovered: true };
234
+ } else if (failureResult?.suppress) {
235
+ content = JSON.stringify({ error: String(error), suppressed: true });
236
+ result = { error: String(error), suppressed: true };
237
+ } else {
238
+ throw error;
239
+ }
240
+ } else {
241
+ throw error;
242
+ }
243
+ }
244
+ await appendToolResult({
245
+ threadId: options.threadId,
246
+ toolCallId: toolCall.id,
247
+ content
248
+ });
249
+ const toolResult = {
250
+ toolCallId: toolCall.id,
251
+ name: toolCall.name,
252
+ result
253
+ };
254
+ if (options.hooks?.onPostToolUse) {
255
+ const durationMs = Date.now() - startTime;
256
+ await options.hooks.onPostToolUse({
257
+ toolCall,
258
+ result: toolResult,
259
+ threadId: options.threadId,
260
+ turn,
261
+ durationMs
262
+ });
263
+ }
264
+ return toolResult;
86
265
  }
87
- const taskTool = createTaskTool(config.subagents);
88
- const taskHandler = createTaskHandler(config.subagents);
89
266
  return {
90
- tools: {
91
- ...userTools,
92
- Task: taskTool
267
+ // --- Methods from registry ---
268
+ hasTools() {
269
+ return toolMap.size > 0;
270
+ },
271
+ parseToolCall(toolCall) {
272
+ const tool = toolMap.get(toolCall.name);
273
+ if (!tool) {
274
+ throw new Error(`Tool ${toolCall.name} not found`);
275
+ }
276
+ const parsedArgs = tool.schema.parse(toolCall.args);
277
+ return {
278
+ id: toolCall.id ?? "",
279
+ name: toolCall.name,
280
+ args: parsedArgs
281
+ };
282
+ },
283
+ hasTool(name) {
284
+ return toolMap.has(name);
285
+ },
286
+ getToolNames() {
287
+ return Array.from(toolMap.keys());
288
+ },
289
+ getToolDefinitions() {
290
+ return Array.from(toolMap).map(([name, tool]) => ({
291
+ name,
292
+ description: tool.description,
293
+ schema: tool.schema,
294
+ strict: tool.strict,
295
+ max_uses: tool.max_uses
296
+ }));
297
+ },
298
+ // --- Methods for processing tool calls ---
299
+ async processToolCalls(toolCalls, context) {
300
+ if (toolCalls.length === 0) {
301
+ return [];
302
+ }
303
+ const turn = context?.turn ?? 0;
304
+ const handlerContext = context?.handlerContext;
305
+ if (options.parallel) {
306
+ const results2 = await Promise.all(
307
+ toolCalls.map((tc) => processToolCall(tc, turn, handlerContext))
308
+ );
309
+ return results2.filter(
310
+ (r) => r !== null
311
+ );
312
+ }
313
+ const results = [];
314
+ for (const toolCall of toolCalls) {
315
+ const result = await processToolCall(toolCall, turn, handlerContext);
316
+ if (result !== null) {
317
+ results.push(result);
318
+ }
319
+ }
320
+ return results;
321
+ },
322
+ async processToolCallsByName(toolCalls, toolName, handler, context) {
323
+ const matchingCalls = toolCalls.filter((tc) => tc.name === toolName);
324
+ if (matchingCalls.length === 0) {
325
+ return [];
326
+ }
327
+ const handlerContext = context?.handlerContext ?? {};
328
+ const processOne = async (toolCall) => {
329
+ const response = await handler(
330
+ toolCall.args,
331
+ handlerContext
332
+ );
333
+ await appendToolResult({
334
+ threadId: options.threadId,
335
+ toolCallId: toolCall.id,
336
+ content: response.content
337
+ });
338
+ return {
339
+ toolCallId: toolCall.id,
340
+ name: toolCall.name,
341
+ result: response.result
342
+ };
343
+ };
344
+ if (options.parallel) {
345
+ return Promise.all(matchingCalls.map(processOne));
346
+ }
347
+ const results = [];
348
+ for (const toolCall of matchingCalls) {
349
+ results.push(await processOne(toolCall));
350
+ }
351
+ return results;
352
+ },
353
+ // --- Utility methods ---
354
+ filterByName(toolCalls, name) {
355
+ return toolCalls.filter(
356
+ (tc) => tc.name === name
357
+ );
358
+ },
359
+ hasToolCall(toolCalls, name) {
360
+ return toolCalls.some((tc) => tc.name === name);
93
361
  },
94
- taskHandler
362
+ getResultsByName(results, name) {
363
+ return results.filter((r) => r.name === name);
364
+ }
95
365
  };
96
366
  }
97
- function hasTaskTool(tools) {
98
- return "Task" in tools;
367
+ function hasNoOtherToolCalls(toolCalls, excludeName) {
368
+ return toolCalls.filter((tc) => tc.name !== excludeName).length === 0;
99
369
  }
100
370
 
101
371
  // src/lib/session.ts
102
- var createSession = async ({ threadId, agentName, maxTurns = 50, metadata = {} }, {
372
+ async function resolvePrompt(prompt) {
373
+ if (typeof prompt === "function") {
374
+ return prompt();
375
+ }
376
+ return prompt;
377
+ }
378
+ var createSession = async ({
379
+ threadId,
380
+ agentName,
381
+ maxTurns = 50,
382
+ metadata = {},
103
383
  runAgent,
104
- promptManager,
105
- toolRouter,
106
- toolRegistry,
384
+ baseSystemPrompt,
385
+ instructionsPrompt,
386
+ buildContextMessage,
387
+ buildFileTree = async () => "",
388
+ subagents,
389
+ tools = {},
390
+ processToolsInParallel = true,
391
+ buildInTools = {},
107
392
  hooks = {}
108
393
  }) => {
109
- const { initializeThread, appendHumanMessage, parseToolCalls } = workflow.proxyActivities({
394
+ const {
395
+ initializeThread,
396
+ appendHumanMessage,
397
+ parseToolCalls,
398
+ appendToolResult,
399
+ appendSystemMessage
400
+ } = proxyActivities({
110
401
  startToCloseTimeout: "30m",
111
402
  retry: {
112
403
  maximumAttempts: 6,
@@ -116,6 +407,16 @@ var createSession = async ({ threadId, agentName, maxTurns = 50, metadata = {} }
116
407
  },
117
408
  heartbeatTimeout: "5m"
118
409
  });
410
+ const fileTree = await buildFileTree();
411
+ const toolRouter = createToolRouter({
412
+ tools,
413
+ threadId,
414
+ hooks,
415
+ buildInTools,
416
+ fileTree,
417
+ subagents,
418
+ parallel: processToolsInParallel
419
+ });
119
420
  const callSessionEnd = async (exitReason, turns) => {
120
421
  if (hooks.onSessionEnd) {
121
422
  await hooks.onSessionEnd({
@@ -128,7 +429,7 @@ var createSession = async ({ threadId, agentName, maxTurns = 50, metadata = {} }
128
429
  }
129
430
  };
130
431
  return {
131
- runSession: async (prompt, stateManager) => {
432
+ runSession: async ({ stateManager }) => {
132
433
  if (hooks.onSessionStart) {
133
434
  await hooks.onSessionStart({
134
435
  threadId,
@@ -136,43 +437,54 @@ var createSession = async ({ threadId, agentName, maxTurns = 50, metadata = {} }
136
437
  metadata
137
438
  });
138
439
  }
440
+ stateManager.setTools(toolRouter.getToolDefinitions());
139
441
  await initializeThread(threadId);
140
- await appendHumanMessage(
442
+ await appendSystemMessage(
141
443
  threadId,
142
- await promptManager.buildContextMessage(prompt)
444
+ [
445
+ await resolvePrompt(baseSystemPrompt),
446
+ await resolvePrompt(instructionsPrompt)
447
+ ].join("\n")
143
448
  );
449
+ await appendHumanMessage(threadId, await buildContextMessage());
144
450
  let exitReason = "completed";
145
451
  try {
146
452
  while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
147
453
  stateManager.incrementTurns();
148
454
  const currentTurn = stateManager.getTurns();
149
- const { message, stopReason } = await runAgent(
150
- {
151
- threadId,
152
- agentName,
153
- metadata
154
- },
155
- {
156
- systemPrompt: await promptManager.getSystemPrompt()
157
- }
158
- );
455
+ const { message, stopReason } = await runAgent({
456
+ threadId,
457
+ agentName,
458
+ metadata
459
+ });
159
460
  if (stopReason === "end_turn") {
160
461
  stateManager.complete();
161
462
  exitReason = "completed";
162
463
  return message;
163
464
  }
164
- if (!toolRouter || !toolRegistry) {
465
+ if (!toolRouter.hasTools()) {
165
466
  stateManager.complete();
166
467
  exitReason = "completed";
167
468
  return message;
168
469
  }
169
470
  const rawToolCalls = await parseToolCalls(message);
170
- const parsedToolCalls = rawToolCalls.map(
171
- (tc) => toolRegistry.parseToolCall(tc)
471
+ const parsedToolCalls = rawToolCalls.filter((tc) => tc.name !== "Task").map((tc) => toolRouter.parseToolCall(tc));
472
+ const taskToolCalls = subagents && subagents.length > 0 ? rawToolCalls.filter((tc) => tc.name === "Task").map((tc) => {
473
+ const parsedArgs = createTaskTool(subagents).schema.parse(
474
+ tc.args
475
+ );
476
+ return {
477
+ id: tc.id ?? "",
478
+ name: tc.name,
479
+ args: parsedArgs
480
+ };
481
+ }) : [];
482
+ await toolRouter.processToolCalls(
483
+ [...parsedToolCalls, ...taskToolCalls],
484
+ {
485
+ turn: currentTurn
486
+ }
172
487
  );
173
- await toolRouter.processToolCalls(parsedToolCalls, {
174
- turn: currentTurn
175
- });
176
488
  if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
177
489
  exitReason = "waiting_for_input";
178
490
  break;
@@ -198,19 +510,34 @@ function isTerminalStatus(status) {
198
510
  }
199
511
 
200
512
  // src/lib/state-manager.ts
201
- function createAgentStateManager(config) {
202
- let status = "RUNNING";
203
- let version = 0;
204
- let turns = 0;
205
- const customState = { ...config?.initialState ?? {} };
513
+ var getStateQuery = defineQuery("getState");
514
+ function createAgentStateManager(initialState) {
515
+ let status = initialState?.status ?? "RUNNING";
516
+ let version = initialState?.version ?? 0;
517
+ let turns = initialState?.turns ?? 0;
518
+ let tools = initialState?.tools ?? [];
519
+ const tasks = new Map(initialState?.tasks);
520
+ const {
521
+ status: _,
522
+ version: __,
523
+ turns: ___,
524
+ tasks: ____,
525
+ tools: _____,
526
+ ...custom
527
+ } = initialState ?? {};
528
+ const customState = custom;
206
529
  function buildState() {
207
530
  return {
208
531
  status,
209
532
  version,
210
533
  turns,
534
+ tools,
211
535
  ...customState
212
536
  };
213
537
  }
538
+ setHandler(getStateQuery, () => {
539
+ return buildState();
540
+ });
214
541
  return {
215
542
  getStatus() {
216
543
  return status;
@@ -265,6 +592,26 @@ function createAgentStateManager(config) {
265
592
  },
266
593
  shouldReturnFromWait(lastKnownVersion) {
267
594
  return version > lastKnownVersion || isTerminalStatus(status);
595
+ },
596
+ getTasks() {
597
+ return Array.from(tasks.values());
598
+ },
599
+ getTask(id) {
600
+ return tasks.get(id);
601
+ },
602
+ setTask(task) {
603
+ tasks.set(task.id, task);
604
+ version++;
605
+ },
606
+ setTools(newTools) {
607
+ tools = newTools;
608
+ },
609
+ deleteTask(id) {
610
+ const deleted = tasks.delete(id);
611
+ if (deleted) {
612
+ version++;
613
+ }
614
+ return deleted;
268
615
  }
269
616
  };
270
617
  }
@@ -273,241 +620,33 @@ var AGENT_HANDLER_NAMES = {
273
620
  waitForStateChange: "waitForStateChange",
274
621
  addMessage: "addMessage"
275
622
  };
623
+ var askUserQuestionTool = {
624
+ name: "AskUserQuestion",
625
+ description: `Use this tool when you need to ask the user questions during execution. This allows you to:
276
626
 
277
- // src/lib/prompt-manager.ts
278
- function createPromptManager(config) {
279
- const { baseSystemPrompt, instructionsPrompt, buildContextMessage } = config;
280
- async function resolvePrompt(prompt) {
281
- if (typeof prompt === "function") {
282
- return prompt();
283
- }
284
- return prompt;
285
- }
286
- return {
287
- async getSystemPrompt() {
288
- const base = await resolvePrompt(baseSystemPrompt);
289
- const instructions = await resolvePrompt(instructionsPrompt);
290
- return [base, instructions].join("\n");
291
- },
292
- async buildContextMessage(context) {
293
- return buildContextMessage(context);
294
- }
295
- };
296
- }
627
+ 1. Gather user preferences or requirements
628
+ 2. Clarify ambiguous instructions
629
+ 3. Get decisions on implementation choices as you work
630
+ 4. Offer choices to the user about what direction to take.
297
631
 
298
- // src/lib/tool-registry.ts
299
- function createToolRegistry(tools) {
300
- const toolMap = /* @__PURE__ */ new Map();
301
- for (const [_key, tool] of Object.entries(tools)) {
302
- toolMap.set(tool.name, tool);
303
- }
304
- return {
305
- parseToolCall(toolCall) {
306
- const tool = toolMap.get(toolCall.name);
307
- if (!tool) {
308
- throw new Error(`Tool ${toolCall.name} not found`);
309
- }
310
- const parsedArgs = tool.schema.parse(toolCall.args);
311
- return {
312
- id: toolCall.id ?? "",
313
- name: toolCall.name,
314
- args: parsedArgs
315
- };
316
- },
317
- getToolList() {
318
- return Object.values(tools);
319
- },
320
- getTool(name) {
321
- return tools[name];
322
- },
323
- hasTool(name) {
324
- return toolMap.has(name);
325
- },
326
- getToolNames() {
327
- return Array.from(toolMap.keys());
328
- }
329
- };
330
- }
632
+ Usage notes:
331
633
 
332
- // src/lib/tool-router.ts
333
- function createToolRouter(options, handlers) {
334
- const { parallel = true, threadId, appendToolResult, hooks } = options;
335
- async function processToolCall(toolCall, turn) {
336
- const startTime = Date.now();
337
- let effectiveArgs = toolCall.args;
338
- if (hooks?.onPreToolUse) {
339
- const preResult = await hooks.onPreToolUse({
340
- toolCall,
341
- threadId,
342
- turn
343
- });
344
- if (preResult?.skip) {
345
- await appendToolResult({
346
- threadId,
347
- toolCallId: toolCall.id,
348
- content: JSON.stringify({
349
- skipped: true,
350
- reason: "Skipped by PreToolUse hook"
351
- })
352
- });
353
- return null;
354
- }
355
- if (preResult?.modifiedArgs !== void 0) {
356
- effectiveArgs = preResult.modifiedArgs;
357
- }
358
- }
359
- const handler = handlers[toolCall.name];
360
- let result;
361
- let content;
362
- try {
363
- if (handler) {
364
- const response = await handler(
365
- effectiveArgs,
366
- toolCall.id
367
- );
368
- result = response.result;
369
- content = response.content;
370
- } else {
371
- result = { error: `Unknown tool: ${toolCall.name}` };
372
- content = JSON.stringify(result, null, 2);
373
- }
374
- } catch (error) {
375
- if (hooks?.onPostToolUseFailure) {
376
- const failureResult = await hooks.onPostToolUseFailure({
377
- toolCall,
378
- error: error instanceof Error ? error : new Error(String(error)),
379
- threadId,
380
- turn
381
- });
382
- if (failureResult?.fallbackContent !== void 0) {
383
- content = failureResult.fallbackContent;
384
- result = { error: String(error), recovered: true };
385
- } else if (failureResult?.suppress) {
386
- content = JSON.stringify({ error: String(error), suppressed: true });
387
- result = { error: String(error), suppressed: true };
388
- } else {
389
- throw error;
390
- }
391
- } else {
392
- throw error;
393
- }
394
- }
395
- await appendToolResult({ threadId, toolCallId: toolCall.id, content });
396
- const toolResult = {
397
- toolCallId: toolCall.id,
398
- name: toolCall.name,
399
- result
400
- };
401
- if (hooks?.onPostToolUse) {
402
- const durationMs = Date.now() - startTime;
403
- await hooks.onPostToolUse({
404
- toolCall,
405
- result: toolResult,
406
- threadId,
407
- turn,
408
- durationMs
409
- });
410
- }
411
- return toolResult;
412
- }
413
- return {
414
- async processToolCalls(toolCalls, context) {
415
- if (toolCalls.length === 0) {
416
- return [];
417
- }
418
- const turn = context?.turn ?? 0;
419
- if (parallel) {
420
- const results2 = await Promise.all(
421
- toolCalls.map((tc) => processToolCall(tc, turn))
422
- );
423
- return results2.filter(
424
- (r) => r !== null
425
- );
426
- }
427
- const results = [];
428
- for (const toolCall of toolCalls) {
429
- const result = await processToolCall(toolCall, turn);
430
- if (result !== null) {
431
- results.push(result);
432
- }
433
- }
434
- return results;
435
- },
436
- async processToolCallsByName(toolCalls, toolName, handler) {
437
- const matchingCalls = toolCalls.filter((tc) => tc.name === toolName);
438
- if (matchingCalls.length === 0) {
439
- return [];
440
- }
441
- const processOne = async (toolCall) => {
442
- const response = await handler(
443
- toolCall.args,
444
- toolCall.id
445
- );
446
- await appendToolResult({
447
- threadId,
448
- toolCallId: toolCall.id,
449
- content: response.content
450
- });
451
- return {
452
- toolCallId: toolCall.id,
453
- name: toolCall.name,
454
- result: response.result
455
- };
456
- };
457
- if (parallel) {
458
- return Promise.all(matchingCalls.map(processOne));
459
- }
460
- const results = [];
461
- for (const toolCall of matchingCalls) {
462
- results.push(await processOne(toolCall));
463
- }
464
- return results;
465
- },
466
- filterByName(toolCalls, name) {
467
- return toolCalls.filter(
468
- (tc) => tc.name === name
469
- );
470
- },
471
- hasToolCall(toolCalls, name) {
472
- return toolCalls.some((tc) => tc.name === name);
473
- },
474
- getResultsByName(results, name) {
475
- return results.filter(
476
- (r) => r.name === name
477
- );
478
- }
479
- };
480
- }
481
- function hasNoOtherToolCalls(toolCalls, excludeName) {
482
- return toolCalls.filter((tc) => tc.name !== excludeName).length === 0;
483
- }
484
- var askUserQuestionTool = {
485
- name: "AskUserQuestion",
486
- description: `Use this tool when you need to ask the user questions during execution. This allows you to:
487
-
488
- 1. Gather user preferences or requirements
489
- 2. Clarify ambiguous instructions
490
- 3. Get decisions on implementation choices as you work
491
- 4. Offer choices to the user about what direction to take.
492
-
493
- Usage notes:
494
-
495
- * Users will always be able to select "Other" to provide custom text input
496
- * Use multiSelect: true to allow multiple answers to be selected for a question
497
- * If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
498
- `,
499
- schema: z2__default.default.object({
500
- questions: z2__default.default.array(
501
- z2__default.default.object({
502
- question: z2__default.default.string().describe("The full question text to display"),
503
- header: z2__default.default.string().describe("Short label for the question (max 12 characters)"),
504
- options: z2__default.default.array(
505
- z2__default.default.object({
506
- label: z2__default.default.string(),
507
- description: z2__default.default.string()
634
+ * Users will always be able to select "Other" to provide custom text input
635
+ * Use multiSelect: true to allow multiple answers to be selected for a question
636
+ * If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
637
+ `,
638
+ schema: z4.object({
639
+ questions: z4.array(
640
+ z4.object({
641
+ question: z4.string().describe("The full question text to display"),
642
+ header: z4.string().describe("Short label for the question (max 12 characters)"),
643
+ options: z4.array(
644
+ z4.object({
645
+ label: z4.string(),
646
+ description: z4.string()
508
647
  })
509
648
  ).min(0).max(4).describe("Array of 0-4 choices, each with label and description"),
510
- multiSelect: z2__default.default.boolean().describe("If true, users can select multiple options")
649
+ multiSelect: z4.boolean().describe("If true, users can select multiple options")
511
650
  })
512
651
  )
513
652
  }),
@@ -527,9 +666,9 @@ Examples:
527
666
  - "**/*.test.ts" - Find all test files recursively
528
667
  - "src/**/*.ts" - Find all TypeScript files in src directory
529
668
  `,
530
- schema: z2.z.object({
531
- pattern: z2.z.string().describe("Glob pattern to match files against"),
532
- root: z2.z.string().optional().describe("Optional root directory to search from")
669
+ schema: z.object({
670
+ pattern: z.string().describe("Glob pattern to match files against"),
671
+ root: z.string().optional().describe("Optional root directory to search from")
533
672
  }),
534
673
  strict: true
535
674
  };
@@ -547,13 +686,13 @@ Examples:
547
686
  - Search for function definitions with "function.*handleClick"
548
687
  - Search case-insensitively with ignoreCase: true
549
688
  `,
550
- schema: z2.z.object({
551
- pattern: z2.z.string().describe("Regex pattern to search for in file contents"),
552
- ignoreCase: z2.z.boolean().optional().describe("Case-insensitive search (default: false)"),
553
- maxMatches: z2.z.number().optional().describe("Maximum number of matches to return (default: 50)"),
554
- includePatterns: z2.z.array(z2.z.string()).optional().describe("Glob patterns to include (e.g., ['*.ts', '*.js'])"),
555
- excludePatterns: z2.z.array(z2.z.string()).optional().describe("Glob patterns to exclude (e.g., ['*.test.ts'])"),
556
- contextLines: z2.z.number().optional().describe("Number of context lines to show around matches")
689
+ schema: z.object({
690
+ pattern: z.string().describe("Regex pattern to search for in file contents"),
691
+ ignoreCase: z.boolean().optional().describe("Case-insensitive search (default: false)"),
692
+ maxMatches: z.number().optional().describe("Maximum number of matches to return (default: 50)"),
693
+ includePatterns: z.array(z.string()).optional().describe("Glob patterns to include (e.g., ['*.ts', '*.js'])"),
694
+ excludePatterns: z.array(z.string()).optional().describe("Glob patterns to exclude (e.g., ['*.test.ts'])"),
695
+ contextLines: z.number().optional().describe("Number of context lines to show around matches")
557
696
  }),
558
697
  strict: true
559
698
  };
@@ -571,12 +710,12 @@ The tool returns the file content in an appropriate format:
571
710
  - Images: Base64-encoded image data
572
711
  - PDFs: Extracted text content
573
712
  `,
574
- schema: z2.z.object({
575
- path: z2.z.string().describe("Virtual path to the file to read"),
576
- offset: z2.z.number().optional().describe(
713
+ schema: z.object({
714
+ path: z.string().describe("Virtual path to the file to read"),
715
+ offset: z.number().optional().describe(
577
716
  "Line number to start reading from (1-indexed, for text files)"
578
717
  ),
579
- limit: z2.z.number().optional().describe("Maximum number of lines to read (for text files)")
718
+ limit: z.number().optional().describe("Maximum number of lines to read (for text files)")
580
719
  }),
581
720
  strict: true
582
721
  };
@@ -594,9 +733,9 @@ IMPORTANT:
594
733
  - This is an atomic write operation - the entire file is replaced
595
734
  - Path must be absolute (e.g., "/docs/readme.md", not "docs/readme.md")
596
735
  `,
597
- schema: z2.z.object({
598
- file_path: z2.z.string().describe("The absolute virtual path to the file to write"),
599
- content: z2.z.string().describe("The content to write to the file")
736
+ schema: z.object({
737
+ file_path: z.string().describe("The absolute virtual path to the file to write"),
738
+ content: z.string().describe("The content to write to the file")
600
739
  }),
601
740
  strict: true
602
741
  };
@@ -616,392 +755,137 @@ IMPORTANT:
616
755
  - The operation fails if old_string is not found
617
756
  - old_string and new_string must be different
618
757
  `,
619
- schema: z2.z.object({
620
- file_path: z2.z.string().describe("The absolute virtual path to the file to modify"),
621
- old_string: z2.z.string().describe("The exact text to replace"),
622
- new_string: z2.z.string().describe(
758
+ schema: z.object({
759
+ file_path: z.string().describe("The absolute virtual path to the file to modify"),
760
+ old_string: z.string().describe("The exact text to replace"),
761
+ new_string: z.string().describe(
623
762
  "The text to replace it with (must be different from old_string)"
624
763
  ),
625
- replace_all: z2.z.boolean().optional().describe(
764
+ replace_all: z.boolean().optional().describe(
626
765
  "If true, replace all occurrences of old_string (default: false)"
627
766
  )
628
767
  }),
629
768
  strict: true
630
769
  };
631
770
 
632
- // src/lib/filesystem/types.ts
633
- function fileContentToMessageContent(content) {
634
- switch (content.type) {
635
- case "text":
636
- return [{ type: "text", text: content.content }];
637
- case "image":
638
- return [
639
- {
640
- type: "image_url",
641
- image_url: {
642
- url: `data:${content.mimeType};base64,${content.data}`
643
- }
644
- }
645
- ];
646
- case "pdf":
647
- return [{ type: "text", text: content.content }];
648
- }
649
- }
650
- var DEFAULT_HEADER = "Available files and directories:";
651
- var DEFAULT_DESCRIPTION = "You have access to the following files. Use the Read, Glob, and Grep tools to explore them.";
652
- function isExcluded(path, excludePatterns) {
653
- if (!excludePatterns || excludePatterns.length === 0) {
654
- return false;
655
- }
656
- return excludePatterns.some((pattern) => minimatch.minimatch(path, pattern));
657
- }
658
- function renderNode(node, options, depth, indent) {
659
- const lines = [];
660
- if (options.maxDepth !== void 0 && depth > options.maxDepth) {
661
- return lines;
662
- }
663
- if (isExcluded(node.path, options.excludePatterns)) {
664
- return lines;
665
- }
666
- const parts = node.path.split("/");
667
- const name = parts[parts.length - 1] || node.path;
668
- let line = indent;
669
- if (node.type === "directory") {
670
- line += `${name}/`;
671
- } else {
672
- line += name;
673
- }
674
- if (options.showMimeTypes && node.mimeType) {
675
- line += ` [${node.mimeType}]`;
676
- }
677
- if (options.showDescriptions !== false && node.description) {
678
- line += ` - ${node.description}`;
679
- }
680
- lines.push(line);
681
- if (node.type === "directory" && node.children) {
682
- const childIndent = indent + " ";
683
- for (const child of node.children) {
684
- lines.push(...renderNode(child, options, depth + 1, childIndent));
685
- }
686
- }
687
- return lines;
771
+ // src/tools/task-create/handler.ts
772
+ function createTaskCreateHandler({
773
+ stateManager,
774
+ idGenerator
775
+ }) {
776
+ return (args) => {
777
+ const task = {
778
+ id: idGenerator(),
779
+ subject: args.subject,
780
+ description: args.description,
781
+ activeForm: args.activeForm,
782
+ status: "pending",
783
+ metadata: args.metadata ?? {},
784
+ blockedBy: [],
785
+ blocks: []
786
+ };
787
+ stateManager.setTask(task);
788
+ return {
789
+ content: JSON.stringify(task, null, 2),
790
+ result: task
791
+ };
792
+ };
688
793
  }
689
- function buildFileTreePrompt(nodes, options = {}) {
690
- const header = options.headerText ?? DEFAULT_HEADER;
691
- const description = options.descriptionText ?? DEFAULT_DESCRIPTION;
692
- const lines = [];
693
- for (const node of nodes) {
694
- lines.push(...renderNode(node, options, 0, ""));
695
- }
696
- if (lines.length === 0) {
697
- return `<file_system>
698
- ${header}
699
-
700
- ${description}
701
-
702
- (no files available)
703
- </file_system>`;
704
- }
705
- return `<file_system>
706
- ${header}
794
+ var taskGetTool = {
795
+ name: "TaskGet",
796
+ description: `Retrieve full task details including dependencies.`,
797
+ schema: z4.object({
798
+ taskId: z4.string().describe("The ID of the task to get")
799
+ })
800
+ };
707
801
 
708
- ${lines.join("\n")}
709
- </file_system>`;
710
- }
711
- function flattenFileTree(nodes) {
712
- const paths = [];
713
- function traverse(node) {
714
- if (node.type === "file") {
715
- paths.push(node.path);
716
- }
717
- if (node.children) {
718
- for (const child of node.children) {
719
- traverse(child);
720
- }
802
+ // src/tools/task-get/handler.ts
803
+ function createTaskGetHandler(stateManager) {
804
+ return (args) => {
805
+ const task = stateManager.getTask(args.taskId) ?? null;
806
+ if (!task) {
807
+ return {
808
+ content: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
809
+ result: null
810
+ };
721
811
  }
722
- }
723
- for (const node of nodes) {
724
- traverse(node);
725
- }
726
- return paths;
812
+ return {
813
+ content: JSON.stringify(task, null, 2),
814
+ result: task
815
+ };
816
+ };
727
817
  }
728
- function isPathInScope(path, scopedNodes) {
729
- const allowedPaths = flattenFileTree(scopedNodes);
730
- return allowedPaths.includes(path);
818
+ var taskListTool = {
819
+ name: "TaskList",
820
+ description: `List all tasks with current state.`,
821
+ schema: z4.object({})
822
+ };
823
+
824
+ // src/tools/task-list/handler.ts
825
+ function createTaskListHandler(stateManager) {
826
+ return (_args) => {
827
+ const taskList = stateManager.getTasks();
828
+ return {
829
+ content: JSON.stringify(taskList, null, 2),
830
+ result: taskList
831
+ };
832
+ };
731
833
  }
732
- function findNodeByPath(path, nodes) {
733
- for (const node of nodes) {
734
- if (node.path === path) {
735
- return node;
736
- }
737
- if (node.children) {
738
- const found = findNodeByPath(path, node.children);
739
- if (found) {
740
- return found;
741
- }
834
+ var taskUpdateTool = {
835
+ name: "TaskUpdate",
836
+ description: `Update status, add blockers, modify details.`,
837
+ schema: z4.object({
838
+ taskId: z4.string().describe("The ID of the task to get"),
839
+ status: z4.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
840
+ addBlockedBy: z4.array(z4.string()).describe("The IDs of the tasks that are blocking this task"),
841
+ addBlocks: z4.array(z4.string()).describe("The IDs of the tasks that this task is blocking")
842
+ })
843
+ };
844
+
845
+ // src/tools/task-update/handler.ts
846
+ function createTaskUpdateHandler(stateManager) {
847
+ return (args) => {
848
+ const task = stateManager.getTask(args.taskId);
849
+ if (!task) {
850
+ return {
851
+ content: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
852
+ result: null
853
+ };
742
854
  }
743
- }
744
- return void 0;
745
- }
746
- var BaseFileSystemProvider = class {
747
- scopedNodes;
748
- allowedPaths;
749
- constructor(scopedNodes) {
750
- this.scopedNodes = scopedNodes;
751
- this.allowedPaths = new Set(flattenFileTree(scopedNodes));
752
- }
753
- /**
754
- * Validate that a path is within the allowed scope.
755
- * Throws an error if the path is not allowed.
756
- */
757
- validatePath(path) {
758
- if (!this.allowedPaths.has(path)) {
759
- throw new Error(`Path "${path}" is not within the allowed scope`);
855
+ if (args.status) {
856
+ task.status = args.status;
760
857
  }
761
- }
762
- /**
763
- * Filter paths by glob pattern.
764
- */
765
- filterByPattern(paths, pattern, root) {
766
- const effectivePattern = root ? `${root}/${pattern}` : pattern;
767
- return paths.filter((path) => {
768
- if (root && !path.startsWith(root)) {
769
- return false;
770
- }
771
- return minimatch.minimatch(path, effectivePattern, { matchBase: true });
772
- });
773
- }
774
- /**
775
- * Get all file paths in scope
776
- */
777
- getAllPaths() {
778
- return Array.from(this.allowedPaths);
779
- }
780
- /**
781
- * Find FileNode by path
782
- */
783
- findNode(path) {
784
- const findInNodes = (nodes) => {
785
- for (const node of nodes) {
786
- if (node.path === path) {
787
- return node;
858
+ if (args.addBlockedBy) {
859
+ for (const blockerId of args.addBlockedBy) {
860
+ if (!task.blockedBy.includes(blockerId)) {
861
+ task.blockedBy.push(blockerId);
788
862
  }
789
- if (node.children) {
790
- const found = findInNodes(node.children);
791
- if (found) return found;
863
+ const blockerTask = stateManager.getTask(blockerId);
864
+ if (blockerTask && !blockerTask.blocks.includes(task.id)) {
865
+ blockerTask.blocks.push(task.id);
866
+ stateManager.setTask(blockerTask);
792
867
  }
793
868
  }
794
- return void 0;
795
- };
796
- return findInNodes(this.scopedNodes);
797
- }
798
- /**
799
- * Read file as text for grep operations.
800
- * Default implementation uses readFile, but can be overridden for efficiency.
801
- */
802
- async readFileAsText(node) {
803
- try {
804
- const content = await this.readFile(node);
805
- if (content.type === "text") {
806
- return content.content;
807
- }
808
- if (content.type === "pdf") {
809
- return content.content;
810
- }
811
- return null;
812
- } catch {
813
- return null;
814
869
  }
815
- }
816
- // ============================================================================
817
- // FileSystemProvider implementation
818
- // ============================================================================
819
- async glob(pattern, root) {
820
- const allPaths = this.getAllPaths();
821
- const matchingPaths = this.filterByPattern(allPaths, pattern, root);
822
- return matchingPaths.map((path) => this.findNode(path)).filter((node) => node !== void 0);
823
- }
824
- async grep(pattern, options) {
825
- const matches = [];
826
- const maxMatches = options?.maxMatches ?? 50;
827
- const flags = options?.ignoreCase ? "gi" : "g";
828
- let regex;
829
- try {
830
- regex = new RegExp(pattern, flags);
831
- } catch {
832
- throw new Error(`Invalid regex pattern: ${pattern}`);
833
- }
834
- let paths = this.getAllPaths();
835
- if (options?.includePatterns?.length) {
836
- const includePatterns = options.includePatterns;
837
- paths = paths.filter(
838
- (path) => includePatterns.some((p) => minimatch.minimatch(path, p, { matchBase: true }))
839
- );
840
- }
841
- if (options?.excludePatterns?.length) {
842
- const excludePatterns = options.excludePatterns;
843
- paths = paths.filter(
844
- (path) => !excludePatterns.some((p) => minimatch.minimatch(path, p, { matchBase: true }))
845
- );
846
- }
847
- for (const path of paths) {
848
- if (matches.length >= maxMatches) break;
849
- const node = this.findNode(path);
850
- if (!node || node.type !== "file") continue;
851
- const text = await this.readFileAsText(node);
852
- if (!text) continue;
853
- const lines = text.split("\n");
854
- for (let i = 0; i < lines.length; i++) {
855
- if (matches.length >= maxMatches) break;
856
- const line = lines[i];
857
- if (line === void 0) continue;
858
- if (regex.test(line)) {
859
- regex.lastIndex = 0;
860
- const match = {
861
- path,
862
- lineNumber: i + 1,
863
- line: line.trim()
864
- };
865
- if (options?.contextLines && options.contextLines > 0) {
866
- const contextBefore = [];
867
- const contextAfter = [];
868
- for (let j = Math.max(0, i - options.contextLines); j < i; j++) {
869
- const contextLine = lines[j];
870
- if (contextLine !== void 0) {
871
- contextBefore.push(contextLine.trim());
872
- }
873
- }
874
- for (let j = i + 1; j <= Math.min(lines.length - 1, i + options.contextLines); j++) {
875
- const contextLine = lines[j];
876
- if (contextLine !== void 0) {
877
- contextAfter.push(contextLine.trim());
878
- }
879
- }
880
- match.contextBefore = contextBefore;
881
- match.contextAfter = contextAfter;
882
- }
883
- matches.push(match);
870
+ if (args.addBlocks) {
871
+ for (const blockedId of args.addBlocks) {
872
+ if (!task.blocks.includes(blockedId)) {
873
+ task.blocks.push(blockedId);
874
+ }
875
+ const blockedTask = stateManager.getTask(blockedId);
876
+ if (blockedTask && !blockedTask.blockedBy.includes(task.id)) {
877
+ blockedTask.blockedBy.push(task.id);
878
+ stateManager.setTask(blockedTask);
884
879
  }
885
880
  }
886
881
  }
887
- return matches;
888
- }
889
- async read(path) {
890
- this.validatePath(path);
891
- const node = this.findNode(path);
892
- if (!node) {
893
- throw new Error(`File not found: ${path}`);
894
- }
895
- if (node.type !== "file") {
896
- throw new Error(`Path is a directory, not a file: ${path}`);
897
- }
898
- return this.readFile(node);
899
- }
900
- async exists(path) {
901
- return this.allowedPaths.has(path);
902
- }
903
- };
904
- var InMemoryFileSystemProvider = class _InMemoryFileSystemProvider extends BaseFileSystemProvider {
905
- files;
906
- constructor(scopedNodes, files) {
907
- super(scopedNodes);
908
- this.files = files;
909
- }
910
- async readFile(node) {
911
- const content = this.files.get(node.path);
912
- if (!content) {
913
- throw new Error(`File not found in storage: ${node.path}`);
914
- }
915
- return content;
916
- }
917
- /**
918
- * Add or update a file in the in-memory storage
919
- */
920
- setFile(path, content) {
921
- this.files.set(path, content);
922
- }
923
- /**
924
- * Write text content to a file
925
- */
926
- async write(path, content) {
927
- this.validatePath(path);
928
- this.files.set(path, { type: "text", content });
929
- }
930
- /**
931
- * Create an InMemoryFileSystemProvider from a simple object map
932
- */
933
- static fromTextFiles(scopedNodes, files) {
934
- const fileMap = /* @__PURE__ */ new Map();
935
- for (const [path, content] of Object.entries(files)) {
936
- fileMap.set(path, { type: "text", content });
937
- }
938
- return new _InMemoryFileSystemProvider(scopedNodes, fileMap);
939
- }
940
- };
941
- var CompositeFileSystemProvider = class extends BaseFileSystemProvider {
942
- backends;
943
- defaultBackend;
944
- defaultResolver;
945
- constructor(scopedNodes, config) {
946
- super(scopedNodes);
947
- this.backends = new Map(Object.entries(config.backends));
948
- this.defaultBackend = config.defaultBackend;
949
- this.defaultResolver = config.defaultResolver;
950
- }
951
- /**
952
- * Get the backend name for a file node
953
- */
954
- getBackendName(node) {
955
- const backend = node.metadata?.backend;
956
- if (typeof backend === "string") {
957
- return backend;
958
- }
959
- return this.defaultBackend;
960
- }
961
- /**
962
- * Resolve content using a resolver (function or provider)
963
- */
964
- async resolveContent(resolver, node) {
965
- if (typeof resolver === "function") {
966
- return resolver(node);
967
- }
968
- return resolver.read(node.path);
969
- }
970
- async readFile(node) {
971
- const backendName = this.getBackendName(node);
972
- if (backendName) {
973
- const config = this.backends.get(backendName);
974
- if (config) {
975
- return this.resolveContent(config.resolver, node);
976
- }
977
- }
978
- if (this.defaultResolver) {
979
- return this.defaultResolver(node);
980
- }
981
- const availableBackends = Array.from(this.backends.keys()).join(", ");
982
- throw new Error(
983
- `No resolver for file "${node.path}". Backend: ${backendName ?? "(none)"}. Available backends: ${availableBackends || "(none)"}`
984
- );
985
- }
986
- /**
987
- * Add or update a backend configuration
988
- */
989
- setBackend(name, config) {
990
- this.backends.set(name, config);
991
- }
992
- /**
993
- * Remove a backend
994
- */
995
- removeBackend(name) {
996
- return this.backends.delete(name);
997
- }
998
- /**
999
- * Check if a backend exists
1000
- */
1001
- hasBackend(name) {
1002
- return this.backends.has(name);
1003
- }
1004
- };
882
+ stateManager.setTask(task);
883
+ return {
884
+ content: JSON.stringify(task, null, 2),
885
+ result: task
886
+ };
887
+ };
888
+ }
1005
889
 
1006
890
  // node_modules/uuid/dist/esm-node/stringify.js
1007
891
  var byteToHex = [];
@@ -1015,13 +899,13 @@ var rnds8Pool = new Uint8Array(256);
1015
899
  var poolPtr = rnds8Pool.length;
1016
900
  function rng() {
1017
901
  if (poolPtr > rnds8Pool.length - 16) {
1018
- crypto__default.default.randomFillSync(rnds8Pool);
902
+ crypto.randomFillSync(rnds8Pool);
1019
903
  poolPtr = 0;
1020
904
  }
1021
905
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
1022
906
  }
1023
907
  var native_default = {
1024
- randomUUID: crypto__default.default.randomUUID
908
+ randomUUID: crypto.randomUUID
1025
909
  };
1026
910
 
1027
911
  // node_modules/uuid/dist/esm-node/v4.js
@@ -1070,13 +954,13 @@ function createThreadManager(config) {
1070
954
  await redis.del(redisKey);
1071
955
  },
1072
956
  createHumanMessage(content) {
1073
- return new messages.HumanMessage({
957
+ return new HumanMessage({
1074
958
  id: v4_default(),
1075
959
  content
1076
960
  }).toDict();
1077
961
  },
1078
962
  createAIMessage(content, kwargs) {
1079
- return new messages.AIMessage({
963
+ return new AIMessage({
1080
964
  id: v4_default(),
1081
965
  content,
1082
966
  additional_kwargs: kwargs ? {
@@ -1087,12 +971,21 @@ function createThreadManager(config) {
1087
971
  }).toDict();
1088
972
  },
1089
973
  createToolMessage(content, toolCallId) {
1090
- return new messages.ToolMessage({
974
+ return new ToolMessage({
1091
975
  // Cast needed due to langchain type compatibility
1092
976
  content,
1093
977
  tool_call_id: toolCallId
1094
978
  }).toDict();
1095
979
  },
980
+ createSystemMessage(content) {
981
+ return new SystemMessage({
982
+ content
983
+ }).toDict();
984
+ },
985
+ async appendSystemMessage(content) {
986
+ const message = this.createSystemMessage(content);
987
+ await this.append([message]);
988
+ },
1096
989
  async appendHumanMessage(content) {
1097
990
  const message = this.createHumanMessage(content);
1098
991
  await this.append([message]);
@@ -1109,6 +1002,10 @@ function createThreadManager(config) {
1109
1002
  }
1110
1003
  function createSharedActivities(redis) {
1111
1004
  return {
1005
+ async appendSystemMessage(threadId, content) {
1006
+ const thread = createThreadManager({ redis, threadId });
1007
+ await thread.appendSystemMessage(content);
1008
+ },
1112
1009
  async appendToolResult(config) {
1113
1010
  const { threadId, toolCallId, content } = config;
1114
1011
  const thread = createThreadManager({ redis, threadId });
@@ -1127,7 +1024,7 @@ function createSharedActivities(redis) {
1127
1024
  await thread.appendHumanMessage(content);
1128
1025
  },
1129
1026
  async parseToolCalls(storedMessage) {
1130
- const message = messages.mapStoredMessageToChatMessage(storedMessage);
1027
+ const message = mapStoredMessageToChatMessage(storedMessage);
1131
1028
  const toolCalls = message.tool_calls ?? [];
1132
1029
  return toolCalls.map((toolCall) => ({
1133
1030
  id: toolCall.id,
@@ -1139,7 +1036,7 @@ function createSharedActivities(redis) {
1139
1036
  }
1140
1037
 
1141
1038
  // src/plugin.ts
1142
- var ZeitlichPlugin = class extends plugin.SimplePlugin {
1039
+ var ZeitlichPlugin = class extends SimplePlugin {
1143
1040
  constructor(options) {
1144
1041
  super({
1145
1042
  name: "ZeitlichPlugin",
@@ -1147,15 +1044,22 @@ var ZeitlichPlugin = class extends plugin.SimplePlugin {
1147
1044
  });
1148
1045
  }
1149
1046
  };
1150
- async function invokeModel(redis, { threadId, agentName, tools }, model, { systemPrompt }) {
1047
+ async function invokeModel({
1048
+ redis,
1049
+ model,
1050
+ client,
1051
+ config: { threadId, agentName }
1052
+ }) {
1151
1053
  const thread = createThreadManager({ redis, threadId });
1152
1054
  const runId = v4_default();
1153
- const messages$1 = await thread.load();
1055
+ const info = Context.current().info;
1056
+ const parentWorkflowId = info.workflowExecution.workflowId;
1057
+ const parentRunId = info.workflowExecution.runId;
1058
+ const handle = client.getHandle(parentWorkflowId, parentRunId);
1059
+ const { tools } = await handle.query(getStateQuery);
1060
+ const messages = await thread.load();
1154
1061
  const response = await model.invoke(
1155
- [
1156
- new messages.SystemMessage(systemPrompt),
1157
- ...messages.mapStoredMessagesToChatMessages(messages$1)
1158
- ],
1062
+ [...mapStoredMessagesToChatMessages(messages)],
1159
1063
  {
1160
1064
  runName: agentName,
1161
1065
  runId,
@@ -1175,8 +1079,8 @@ async function invokeModel(redis, { threadId, agentName, tools }, model, { syste
1175
1079
  };
1176
1080
  }
1177
1081
  var handleAskUserQuestionToolResult = async (args) => {
1178
- const messages$1 = args.questions.map(
1179
- ({ question, header, options, multiSelect }) => new messages.AIMessage({
1082
+ const messages = args.questions.map(
1083
+ ({ question, header, options, multiSelect }) => new AIMessage({
1180
1084
  content: question,
1181
1085
  additional_kwargs: {
1182
1086
  header,
@@ -1185,292 +1089,37 @@ var handleAskUserQuestionToolResult = async (args) => {
1185
1089
  }
1186
1090
  }).toDict()
1187
1091
  );
1188
- return { content: "Question submitted", result: { chatMessages: messages$1 } };
1092
+ return { content: "Question submitted", result: { chatMessages: messages } };
1189
1093
  };
1190
-
1191
- // src/tools/glob/handler.ts
1192
- function createGlobHandler(config) {
1193
- return async (args) => {
1194
- const { pattern, root } = args;
1195
- try {
1196
- const matches = await config.provider.glob(pattern, root);
1197
- if (matches.length === 0) {
1198
- return {
1199
- content: `No files found matching pattern: ${pattern}`,
1200
- result: { files: [] }
1201
- };
1202
- }
1203
- const paths = matches.map((node) => node.path);
1204
- const fileList = paths.map((p) => ` ${p}`).join("\n");
1205
- return {
1206
- content: `Found ${matches.length} file(s) matching "${pattern}":
1207
- ${fileList}`,
1208
- result: { files: matches }
1209
- };
1210
- } catch (error) {
1211
- const message = error instanceof Error ? error.message : "Unknown error";
1212
- return {
1213
- content: `Error searching for files: ${message}`,
1214
- result: { files: [] }
1215
- };
1216
- }
1217
- };
1218
- }
1219
-
1220
- // src/tools/grep/handler.ts
1221
- function formatMatch(match, showContext) {
1222
- const lines = [];
1223
- if (showContext && match.contextBefore?.length) {
1224
- for (let i = 0; i < match.contextBefore.length; i++) {
1225
- const lineNum = match.lineNumber - match.contextBefore.length + i;
1226
- lines.push(`${match.path}:${lineNum}-${match.contextBefore[i]}`);
1227
- }
1228
- }
1229
- lines.push(`${match.path}:${match.lineNumber}:${match.line}`);
1230
- if (showContext && match.contextAfter?.length) {
1231
- for (let i = 0; i < match.contextAfter.length; i++) {
1232
- const lineNum = match.lineNumber + 1 + i;
1233
- lines.push(`${match.path}:${lineNum}-${match.contextAfter[i]}`);
1234
- }
1235
- }
1236
- return lines.join("\n");
1237
- }
1238
- function createGrepHandler(config) {
1239
- return async (args) => {
1240
- const {
1241
- pattern,
1242
- ignoreCase,
1243
- maxMatches,
1244
- includePatterns,
1245
- excludePatterns,
1246
- contextLines
1247
- } = args;
1248
- try {
1249
- const matches = await config.provider.grep(pattern, {
1250
- ignoreCase,
1251
- maxMatches: maxMatches ?? 50,
1252
- includePatterns,
1253
- excludePatterns,
1254
- contextLines
1255
- });
1256
- if (matches.length === 0) {
1257
- return {
1258
- content: `No matches found for pattern: ${pattern}`,
1259
- result: { matches: [] }
1260
- };
1261
- }
1262
- const showContext = contextLines !== void 0 && contextLines > 0;
1263
- const formattedMatches = matches.map((m) => formatMatch(m, showContext)).join("\n");
1264
- return {
1265
- content: `Found ${matches.length} match(es) for "${pattern}":
1266
-
1267
- ${formattedMatches}`,
1268
- result: { matches }
1269
- };
1270
- } catch (error) {
1271
- const message = error instanceof Error ? error.message : "Unknown error";
1272
- return {
1273
- content: `Error searching file contents: ${message}`,
1274
- result: { matches: [] }
1275
- };
1276
- }
1277
- };
1278
- }
1279
-
1280
- // src/tools/read/handler.ts
1281
- function applyTextRange(content, offset, limit) {
1282
- if (offset === void 0 && limit === void 0) {
1283
- return content;
1284
- }
1285
- const lines = content.split("\n");
1286
- const startLine = offset !== void 0 ? Math.max(0, offset - 1) : 0;
1287
- const endLine = limit !== void 0 ? startLine + limit : lines.length;
1288
- const selectedLines = lines.slice(startLine, endLine);
1289
- return selectedLines.map((line, i) => {
1290
- const lineNum = (startLine + i + 1).toString().padStart(6, " ");
1291
- return `${lineNum}|${line}`;
1292
- }).join("\n");
1293
- }
1294
- function createReadHandler(config) {
1295
- return async (args) => {
1296
- const { path, offset, limit } = args;
1297
- if (!isPathInScope(path, config.scopedNodes)) {
1298
- return {
1299
- content: [
1300
- {
1301
- type: "text",
1302
- text: `Error: Path "${path}" is not within the available file system scope.`
1303
- }
1304
- ],
1305
- result: {
1306
- path,
1307
- content: {
1308
- type: "text",
1309
- content: "Error: Path is not within the available file system scope."
1310
- }
1311
- }
1312
- };
1313
- }
1314
- try {
1315
- const exists = await config.provider.exists(path);
1316
- if (!exists) {
1317
- return {
1318
- content: [
1319
- {
1320
- type: "text",
1321
- text: `Error: File "${path}" does not exist.`
1322
- }
1323
- ],
1324
- result: {
1325
- path,
1326
- content: { type: "text", content: "Error: File does not exist." }
1327
- }
1328
- };
1329
- }
1330
- const fileContent = await config.provider.read(path);
1331
- if (fileContent.type === "text") {
1332
- const processedContent = applyTextRange(
1333
- fileContent.content,
1334
- offset,
1335
- limit
1336
- );
1337
- let header = `File: ${path}`;
1338
- if (offset !== void 0 || limit !== void 0) {
1339
- const startLine = offset ?? 1;
1340
- const endInfo = limit ? `, showing ${limit} lines` : "";
1341
- header += ` (from line ${startLine}${endInfo})`;
1342
- }
1343
- return {
1344
- content: [
1345
- {
1346
- type: "text",
1347
- text: `${header}
1348
-
1349
- ${processedContent}`
1350
- }
1351
- ],
1352
- result: {
1353
- path,
1354
- content: {
1355
- type: "text",
1356
- content: `${header}
1357
-
1358
- ${processedContent}`
1359
- }
1360
- }
1361
- };
1362
- }
1363
- const messageContent = fileContentToMessageContent(fileContent);
1364
- return {
1365
- content: [
1366
- {
1367
- type: "text",
1368
- text: `File: ${path} (${fileContent.type})`
1369
- },
1370
- ...messageContent
1371
- ],
1372
- result: { path, content: fileContent }
1373
- };
1374
- } catch (error) {
1375
- const message = error instanceof Error ? error.message : "Unknown error";
1376
- return {
1377
- content: [
1378
- {
1379
- type: "text",
1380
- text: `Error reading file "${path}": ${message}`
1381
- }
1382
- ],
1383
- result: {
1384
- path,
1385
- content: {
1386
- type: "text",
1387
- content: `Error reading file "${path}": ${message}`
1388
- }
1389
- }
1390
- };
1391
- }
1392
- };
1393
- }
1394
-
1395
- // src/tools/write/handler.ts
1396
- function createWriteHandler(config) {
1397
- return async (args) => {
1398
- const { file_path, content } = args;
1399
- if (!isPathInScope(file_path, config.scopedNodes)) {
1400
- return {
1401
- content: `Error: Path "${file_path}" is not within the available file system scope.`,
1402
- result: {
1403
- path: file_path,
1404
- success: false,
1405
- created: false,
1406
- bytesWritten: 0
1407
- }
1408
- };
1409
- }
1410
- if (!config.skipReadCheck && !config.readFiles.has(file_path)) {
1411
- const exists = await config.provider.exists(file_path);
1412
- if (exists) {
1413
- return {
1414
- content: `Error: You must read "${file_path}" before writing to it. Use FileRead first.`,
1415
- result: {
1416
- path: file_path,
1417
- success: false,
1418
- created: false,
1419
- bytesWritten: 0
1420
- }
1421
- };
1422
- }
1423
- }
1424
- try {
1425
- const exists = await config.provider.exists(file_path);
1426
- if (!config.provider.write) {
1427
- return {
1428
- content: `Error: The file system provider does not support write operations.`,
1429
- result: {
1430
- path: file_path,
1431
- success: false,
1432
- created: false,
1433
- bytesWritten: 0
1434
- }
1435
- };
1436
- }
1437
- await config.provider.write(file_path, content);
1438
- const bytesWritten = Buffer.byteLength(content, "utf-8");
1439
- const action = exists ? "Updated" : "Created";
1440
- return {
1441
- content: `${action} file: ${file_path} (${bytesWritten} bytes)`,
1442
- result: {
1443
- path: file_path,
1444
- success: true,
1445
- created: !exists,
1446
- bytesWritten
1447
- }
1448
- };
1449
- } catch (error) {
1450
- const message = error instanceof Error ? error.message : "Unknown error";
1451
- return {
1452
- content: `Error writing file "${file_path}": ${message}`,
1453
- result: {
1454
- path: file_path,
1455
- success: false,
1456
- created: false,
1457
- bytesWritten: 0
1458
- }
1459
- };
1460
- }
1461
- };
1094
+ async function globHandler(_args, fs) {
1095
+ new Bash({ fs });
1096
+ return Promise.resolve({
1097
+ content: "Hello, world!",
1098
+ result: { files: [] }
1099
+ });
1462
1100
  }
1463
1101
 
1464
1102
  // src/tools/edit/handler.ts
1465
1103
  function escapeRegExp(str) {
1466
1104
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1467
1105
  }
1468
- function createEditHandler(config) {
1469
- return async (args) => {
1470
- const { file_path, old_string, new_string, replace_all = false } = args;
1471
- if (old_string === new_string) {
1106
+ async function editHandler(args, fs) {
1107
+ const { file_path, old_string, new_string, replace_all = false } = args;
1108
+ if (old_string === new_string) {
1109
+ return {
1110
+ content: `Error: old_string and new_string must be different.`,
1111
+ result: {
1112
+ path: file_path,
1113
+ success: false,
1114
+ replacements: 0
1115
+ }
1116
+ };
1117
+ }
1118
+ try {
1119
+ const exists = await fs.exists(file_path);
1120
+ if (!exists) {
1472
1121
  return {
1473
- content: `Error: old_string and new_string must be different.`,
1122
+ content: `Error: File "${file_path}" does not exist.`,
1474
1123
  result: {
1475
1124
  path: file_path,
1476
1125
  success: false,
@@ -1478,9 +1127,10 @@ function createEditHandler(config) {
1478
1127
  }
1479
1128
  };
1480
1129
  }
1481
- if (!isPathInScope(file_path, config.scopedNodes)) {
1130
+ const content = await fs.readFile(file_path);
1131
+ if (!content.includes(old_string)) {
1482
1132
  return {
1483
- content: `Error: Path "${file_path}" is not within the available file system scope.`,
1133
+ content: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
1484
1134
  result: {
1485
1135
  path: file_path,
1486
1136
  success: false,
@@ -1488,9 +1138,12 @@ function createEditHandler(config) {
1488
1138
  }
1489
1139
  };
1490
1140
  }
1491
- if (!config.skipReadCheck && !config.readFiles.has(file_path)) {
1141
+ const escapedOldString = escapeRegExp(old_string);
1142
+ const globalRegex = new RegExp(escapedOldString, "g");
1143
+ const occurrences = (content.match(globalRegex) || []).length;
1144
+ if (!replace_all && occurrences > 1) {
1492
1145
  return {
1493
- content: `Error: You must read "${file_path}" before editing it. Use FileRead first.`,
1146
+ content: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
1494
1147
  result: {
1495
1148
  path: file_path,
1496
1149
  success: false,
@@ -1498,131 +1151,128 @@ function createEditHandler(config) {
1498
1151
  }
1499
1152
  };
1500
1153
  }
1501
- try {
1502
- const exists = await config.provider.exists(file_path);
1503
- if (!exists) {
1504
- return {
1505
- content: `Error: File "${file_path}" does not exist.`,
1506
- result: {
1507
- path: file_path,
1508
- success: false,
1509
- replacements: 0
1510
- }
1511
- };
1512
- }
1513
- if (!config.provider.write) {
1514
- return {
1515
- content: `Error: The file system provider does not support write operations.`,
1516
- result: {
1517
- path: file_path,
1518
- success: false,
1519
- replacements: 0
1520
- }
1521
- };
1522
- }
1523
- const fileContent = await config.provider.read(file_path);
1524
- if (fileContent.type !== "text") {
1525
- return {
1526
- content: `Error: FileEdit only works with text files. "${file_path}" is ${fileContent.type}.`,
1527
- result: {
1528
- path: file_path,
1529
- success: false,
1530
- replacements: 0
1531
- }
1532
- };
1533
- }
1534
- const content = fileContent.content;
1535
- if (!content.includes(old_string)) {
1536
- return {
1537
- content: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
1538
- result: {
1539
- path: file_path,
1540
- success: false,
1541
- replacements: 0
1542
- }
1543
- };
1544
- }
1545
- const escapedOldString = escapeRegExp(old_string);
1546
- const globalRegex = new RegExp(escapedOldString, "g");
1547
- const occurrences = (content.match(globalRegex) || []).length;
1548
- if (!replace_all && occurrences > 1) {
1549
- return {
1550
- content: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
1551
- result: {
1552
- path: file_path,
1553
- success: false,
1554
- replacements: 0
1555
- }
1556
- };
1154
+ let newContent;
1155
+ let replacements;
1156
+ if (replace_all) {
1157
+ newContent = content.split(old_string).join(new_string);
1158
+ replacements = occurrences;
1159
+ } else {
1160
+ const index = content.indexOf(old_string);
1161
+ newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
1162
+ replacements = 1;
1163
+ }
1164
+ await fs.writeFile(file_path, newContent);
1165
+ const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
1166
+ return {
1167
+ content: `${summary} in ${file_path}`,
1168
+ result: {
1169
+ path: file_path,
1170
+ success: true,
1171
+ replacements
1557
1172
  }
1558
- let newContent;
1559
- let replacements;
1560
- if (replace_all) {
1561
- newContent = content.split(old_string).join(new_string);
1562
- replacements = occurrences;
1563
- } else {
1564
- const index = content.indexOf(old_string);
1565
- newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
1566
- replacements = 1;
1173
+ };
1174
+ } catch (error) {
1175
+ const message = error instanceof Error ? error.message : "Unknown error";
1176
+ return {
1177
+ content: `Error editing file "${file_path}": ${message}`,
1178
+ result: {
1179
+ path: file_path,
1180
+ success: false,
1181
+ replacements: 0
1567
1182
  }
1568
- await config.provider.write(file_path, newContent);
1569
- const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
1570
- return {
1571
- content: `${summary} in ${file_path}`,
1572
- result: {
1573
- path: file_path,
1574
- success: true,
1575
- replacements
1576
- }
1577
- };
1578
- } catch (error) {
1579
- const message = error instanceof Error ? error.message : "Unknown error";
1580
- return {
1581
- content: `Error editing file "${file_path}": ${message}`,
1582
- result: {
1583
- path: file_path,
1584
- success: false,
1585
- replacements: 0
1183
+ };
1184
+ }
1185
+ }
1186
+ var handleBashTool = (fs) => async (args, _context) => {
1187
+ const { command } = args;
1188
+ const bashOptions = fs ? { fs } : {};
1189
+ const bash = new Bash(bashOptions);
1190
+ try {
1191
+ const { exitCode, stderr, stdout } = await bash.exec(command);
1192
+ const bashExecOut = { exitCode, stderr, stdout };
1193
+ return {
1194
+ content: `Exit code: ${exitCode}
1195
+
1196
+ stdout:
1197
+ ${stdout}
1198
+
1199
+ stderr:
1200
+ ${stderr}`,
1201
+ result: bashExecOut
1202
+ };
1203
+ } catch (error) {
1204
+ const err = error instanceof Error ? error : new Error("Unknown error");
1205
+ return {
1206
+ content: `Error executing bash command: ${err.message}`,
1207
+ result: null
1208
+ };
1209
+ }
1210
+ };
1211
+
1212
+ // src/lib/fs.ts
1213
+ var basename = (path, separator) => {
1214
+ if (path[path.length - 1] === separator) path = path.slice(0, -1);
1215
+ const lastSlashIndex = path.lastIndexOf(separator);
1216
+ return lastSlashIndex === -1 ? path : path.slice(lastSlashIndex + 1);
1217
+ };
1218
+ var printTree = async (tab = "", children) => {
1219
+ let str = "";
1220
+ let last = children.length - 1;
1221
+ for (; last >= 0; last--) if (children[last]) break;
1222
+ for (let i = 0; i <= last; i++) {
1223
+ const fn = children[i];
1224
+ if (!fn) continue;
1225
+ const isLast = i === last;
1226
+ const child = await fn(tab + (isLast ? " " : "\u2502") + " ");
1227
+ const branch = child ? isLast ? "\u2514\u2500" : "\u251C\u2500" : "\u2502";
1228
+ str += "\n" + tab + branch + (child ? " " + child : "");
1229
+ }
1230
+ return str;
1231
+ };
1232
+ var toTree = async (fs, opts = {}) => {
1233
+ const separator = opts.separator || "/";
1234
+ let dir = opts.dir || separator;
1235
+ if (dir[dir.length - 1] !== separator) dir += separator;
1236
+ const tab = opts.tab || "";
1237
+ const depth = opts.depth ?? 10;
1238
+ const sort = opts.sort ?? true;
1239
+ let subtree = " (...)";
1240
+ if (depth > 0) {
1241
+ const list = await fs.readdirWithFileTypes?.(dir) || [];
1242
+ if (sort) {
1243
+ list.sort((a, b) => {
1244
+ if (a.isDirectory && b.isDirectory) {
1245
+ return a.name.toString().localeCompare(b.name.toString());
1246
+ } else if (a.isDirectory) {
1247
+ return -1;
1248
+ } else if (b.isDirectory) {
1249
+ return 1;
1250
+ } else {
1251
+ return a.name.toString().localeCompare(b.name.toString());
1586
1252
  }
1587
- };
1253
+ });
1588
1254
  }
1589
- };
1590
- }
1255
+ subtree = await printTree(
1256
+ tab,
1257
+ list.map((entry) => async (tab2) => {
1258
+ if (entry.isDirectory) {
1259
+ return toTree(fs, {
1260
+ dir: dir + entry.name,
1261
+ depth: depth - 1,
1262
+ tab: tab2
1263
+ });
1264
+ } else if (entry.isSymbolicLink) {
1265
+ return "" + entry.name + " \u2192 " + await fs.readlink(dir + entry.name);
1266
+ } else {
1267
+ return "" + entry.name;
1268
+ }
1269
+ })
1270
+ );
1271
+ }
1272
+ const base = basename(dir, separator) + separator;
1273
+ return base + subtree;
1274
+ };
1591
1275
 
1592
- exports.AGENT_HANDLER_NAMES = AGENT_HANDLER_NAMES;
1593
- exports.BaseFileSystemProvider = BaseFileSystemProvider;
1594
- exports.CompositeFileSystemProvider = CompositeFileSystemProvider;
1595
- exports.InMemoryFileSystemProvider = InMemoryFileSystemProvider;
1596
- exports.ZeitlichPlugin = ZeitlichPlugin;
1597
- exports.askUserQuestionTool = askUserQuestionTool;
1598
- exports.buildFileTreePrompt = buildFileTreePrompt;
1599
- exports.createAgentStateManager = createAgentStateManager;
1600
- exports.createEditHandler = createEditHandler;
1601
- exports.createGlobHandler = createGlobHandler;
1602
- exports.createGrepHandler = createGrepHandler;
1603
- exports.createPromptManager = createPromptManager;
1604
- exports.createReadHandler = createReadHandler;
1605
- exports.createSession = createSession;
1606
- exports.createSharedActivities = createSharedActivities;
1607
- exports.createTaskHandler = createTaskHandler;
1608
- exports.createTaskTool = createTaskTool;
1609
- exports.createToolRegistry = createToolRegistry;
1610
- exports.createToolRouter = createToolRouter;
1611
- exports.createWriteHandler = createWriteHandler;
1612
- exports.editTool = editTool;
1613
- exports.fileContentToMessageContent = fileContentToMessageContent;
1614
- exports.findNodeByPath = findNodeByPath;
1615
- exports.flattenFileTree = flattenFileTree;
1616
- exports.globTool = globTool;
1617
- exports.grepTool = grepTool;
1618
- exports.handleAskUserQuestionToolResult = handleAskUserQuestionToolResult;
1619
- exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
1620
- exports.hasTaskTool = hasTaskTool;
1621
- exports.invokeModel = invokeModel;
1622
- exports.isPathInScope = isPathInScope;
1623
- exports.isTerminalStatus = isTerminalStatus;
1624
- exports.readTool = readTool;
1625
- exports.withSubagentSupport = withSubagentSupport;
1626
- exports.writeTool = writeTool;
1276
+ export { AGENT_HANDLER_NAMES, ZeitlichPlugin, askUserQuestionTool, bashTool, createAgentStateManager, createSession, createSharedActivities, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskTool, createTaskUpdateHandler, createToolRouter, editHandler, editTool, globHandler, globTool, grepTool, handleAskUserQuestionToolResult, handleBashTool, hasNoOtherToolCalls, invokeModel, isTerminalStatus, readTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, writeTool };
1627
1277
  //# sourceMappingURL=index.js.map
1628
1278
  //# sourceMappingURL=index.js.map