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