zeitlich 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -33
- package/dist/index.cjs +445 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -42
- package/dist/index.d.ts +25 -42
- package/dist/index.js +415 -362
- package/dist/index.js.map +1 -1
- package/dist/{workflow-CCoHnc3B.d.cts → workflow-D-2vp4Pq.d.cts} +456 -253
- package/dist/{workflow-CCoHnc3B.d.ts → workflow-D-2vp4Pq.d.ts} +456 -253
- package/dist/workflow.cjs +339 -272
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +4 -3
- package/dist/workflow.d.ts +4 -3
- package/dist/workflow.js +315 -252
- package/dist/workflow.js.map +1 -1
- package/package.json +3 -2
- package/src/activities.ts +1 -14
- package/src/index.ts +17 -11
- package/src/lib/session.ts +69 -86
- package/src/lib/state-manager.ts +9 -2
- package/src/lib/thread-manager.ts +45 -37
- package/src/lib/tool-router.ts +338 -116
- package/src/lib/types.ts +110 -28
- package/src/tools/ask-user-question/handler.ts +6 -6
- package/src/tools/ask-user-question/tool.ts +3 -2
- package/src/tools/bash/bash.test.ts +32 -32
- package/src/tools/bash/handler.ts +9 -9
- 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/handler.ts +19 -9
- package/src/tools/task/tool.ts +3 -10
- package/src/tools/task-create/handler.ts +11 -20
- package/src/tools/task-create/tool.ts +3 -2
- package/src/tools/task-get/handler.ts +9 -14
- package/src/tools/task-get/tool.ts +3 -2
- package/src/tools/task-list/handler.ts +7 -12
- package/src/tools/task-list/tool.ts +3 -2
- package/src/tools/task-update/handler.ts +9 -16
- package/src/tools/task-update/tool.ts +3 -2
- package/src/tools/{write → write-file}/tool.ts +5 -6
- package/src/workflow.ts +25 -19
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { defineQuery, proxyActivities, setHandler,
|
|
2
|
-
import
|
|
1
|
+
import { defineQuery, proxyActivities, setHandler, uuid4, workflowInfo, executeChild } from '@temporalio/workflow';
|
|
2
|
+
import z3, { z } from 'zod';
|
|
3
3
|
import { SimplePlugin } from '@temporalio/plugin';
|
|
4
|
-
import { mapStoredMessageToChatMessage, mapStoredMessagesToChatMessages, AIMessage,
|
|
4
|
+
import { mapStoredMessageToChatMessage, mapStoredMessagesToChatMessages, AIMessage, ToolMessage, HumanMessage } from '@langchain/core/messages';
|
|
5
5
|
import crypto from 'crypto';
|
|
6
6
|
import { Context } from '@temporalio/activity';
|
|
7
7
|
import { Bash } from 'just-bash';
|
|
@@ -39,10 +39,10 @@ function createTaskTool(subagents) {
|
|
|
39
39
|
return {
|
|
40
40
|
name: TASK_TOOL,
|
|
41
41
|
description: buildTaskDescription(subagents),
|
|
42
|
-
schema:
|
|
43
|
-
subagent:
|
|
44
|
-
description:
|
|
45
|
-
prompt:
|
|
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
|
}
|
|
@@ -56,149 +56,67 @@ function createTaskHandler(subagents) {
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
const childWorkflowId = `${parentWorkflowId}-${args.subagent}-${uuid4()}`;
|
|
59
|
-
const
|
|
59
|
+
const input = {
|
|
60
|
+
prompt: args.prompt,
|
|
61
|
+
...config.context && { context: config.context }
|
|
62
|
+
};
|
|
63
|
+
const childOpts = {
|
|
60
64
|
workflowId: childWorkflowId,
|
|
61
|
-
args: [
|
|
65
|
+
args: [input],
|
|
62
66
|
taskQueue: config.taskQueue ?? parentTaskQueue
|
|
63
|
-
}
|
|
67
|
+
};
|
|
68
|
+
const childResult = typeof config.workflow === "string" ? await executeChild(config.workflow, childOpts) : await executeChild(config.workflow, childOpts);
|
|
64
69
|
const validated = config.resultSchema ? config.resultSchema.parse(childResult) : childResult;
|
|
65
|
-
const
|
|
70
|
+
const toolResponse = typeof validated === "string" ? validated : JSON.stringify(validated, null, 2);
|
|
66
71
|
return {
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
toolResponse,
|
|
73
|
+
data: {
|
|
69
74
|
result: validated,
|
|
70
75
|
childWorkflowId
|
|
71
76
|
}
|
|
72
77
|
};
|
|
73
78
|
};
|
|
74
79
|
}
|
|
75
|
-
var createBashToolDescription = ({
|
|
76
|
-
fileTree
|
|
77
|
-
}) => `Execute shell commands in a bash environment.
|
|
78
|
-
|
|
79
|
-
Use this tool to:
|
|
80
|
-
- Run shell commands (ls, cat, grep, find, etc.)
|
|
81
|
-
- Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
|
|
82
|
-
- Inspect files and directories
|
|
83
|
-
|
|
84
|
-
Current file tree:
|
|
85
|
-
${fileTree}`;
|
|
86
|
-
var bashTool = {
|
|
87
|
-
name: "Bash",
|
|
88
|
-
description: `Execute shell commands in a sandboxed bash environment.
|
|
89
|
-
|
|
90
|
-
Use this tool to:
|
|
91
|
-
- Run shell commands (ls, cat, grep, find, etc.)
|
|
92
|
-
- Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
|
|
93
|
-
- Inspect files and directories
|
|
94
|
-
`,
|
|
95
|
-
schema: z4.object({
|
|
96
|
-
command: z4.string().describe(
|
|
97
|
-
"The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
|
|
98
|
-
)
|
|
99
|
-
}),
|
|
100
|
-
strict: true
|
|
101
|
-
};
|
|
102
|
-
var taskCreateTool = {
|
|
103
|
-
name: "TaskCreate",
|
|
104
|
-
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.
|
|
105
|
-
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
106
|
-
|
|
107
|
-
## When to Use This Tool
|
|
108
|
-
|
|
109
|
-
Use this tool proactively in these scenarios:
|
|
110
|
-
|
|
111
|
-
- Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
112
|
-
- Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
113
|
-
- User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
114
|
-
- User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
115
|
-
- After receiving new instructions - Immediately capture user requirements as tasks
|
|
116
|
-
- When you start working on a task - Mark it as in_progress BEFORE beginning work
|
|
117
|
-
- After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
|
|
118
|
-
|
|
119
|
-
## When NOT to Use This Tool
|
|
120
|
-
|
|
121
|
-
Skip using this tool when:
|
|
122
|
-
- There is only a single, straightforward task
|
|
123
|
-
- The task is trivial and tracking it provides no organizational benefit
|
|
124
|
-
- The task can be completed in less than 3 trivial steps
|
|
125
|
-
- The task is purely conversational or informational
|
|
126
|
-
|
|
127
|
-
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.
|
|
128
|
-
|
|
129
|
-
## Task Fields
|
|
130
|
-
|
|
131
|
-
- **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
|
|
132
|
-
- **description**: Detailed description of what needs to be done, including context and acceptance criteria
|
|
133
|
-
- **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.
|
|
134
|
-
|
|
135
|
-
**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\`.
|
|
136
|
-
|
|
137
|
-
## Tips
|
|
138
|
-
|
|
139
|
-
- Create tasks with clear, specific subjects that describe the outcome
|
|
140
|
-
- Include enough detail in the description for another agent to understand and complete the task
|
|
141
|
-
- After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
|
|
142
|
-
- Check TaskList first to avoid creating duplicate tasks`,
|
|
143
|
-
schema: z4.object({
|
|
144
|
-
subject: z4.string().describe(
|
|
145
|
-
'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
|
|
146
|
-
),
|
|
147
|
-
description: z4.string().describe(
|
|
148
|
-
"Detailed description of what needs to be done, including context and acceptance criteria"
|
|
149
|
-
),
|
|
150
|
-
activeForm: z4.string().describe(
|
|
151
|
-
'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.'
|
|
152
|
-
),
|
|
153
|
-
metadata: z4.record(z4.string(), z4.string()).describe("Arbitrary key-value pairs for tracking")
|
|
154
|
-
})
|
|
155
|
-
};
|
|
156
80
|
|
|
157
81
|
// src/lib/tool-router.ts
|
|
158
|
-
var buildIntoolDefinitions = {
|
|
159
|
-
[bashTool.name]: bashTool,
|
|
160
|
-
[taskCreateTool.name]: taskCreateTool
|
|
161
|
-
};
|
|
162
82
|
function createToolRouter(options) {
|
|
163
|
-
const { appendToolResult } =
|
|
164
|
-
startToCloseTimeout: "2m",
|
|
165
|
-
retry: {
|
|
166
|
-
maximumAttempts: 3,
|
|
167
|
-
initialInterval: "5s",
|
|
168
|
-
maximumInterval: "15m",
|
|
169
|
-
backoffCoefficient: 4
|
|
170
|
-
}
|
|
171
|
-
});
|
|
83
|
+
const { appendToolResult } = options;
|
|
172
84
|
const toolMap = /* @__PURE__ */ new Map();
|
|
173
85
|
for (const [_key, tool] of Object.entries(options.tools)) {
|
|
174
86
|
toolMap.set(tool.name, tool);
|
|
175
87
|
}
|
|
88
|
+
const isEnabled = (tool) => tool.enabled !== false;
|
|
176
89
|
if (options.subagents) {
|
|
90
|
+
const subagentHooksMap = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const s of options.subagents) {
|
|
92
|
+
if (s.hooks) subagentHooksMap.set(s.name, s.hooks);
|
|
93
|
+
}
|
|
94
|
+
const resolveSubagentName = (args) => args.subagent;
|
|
177
95
|
toolMap.set("Task", {
|
|
178
96
|
...createTaskTool(options.subagents),
|
|
179
|
-
handler: createTaskHandler(options.subagents)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
handler: value
|
|
196
|
-
});
|
|
97
|
+
handler: createTaskHandler(options.subagents),
|
|
98
|
+
...subagentHooksMap.size > 0 && {
|
|
99
|
+
hooks: {
|
|
100
|
+
onPreToolUse: async (ctx) => {
|
|
101
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
102
|
+
return hooks?.onPreExecution?.(ctx) ?? {};
|
|
103
|
+
},
|
|
104
|
+
onPostToolUse: async (ctx) => {
|
|
105
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
106
|
+
await hooks?.onPostExecution?.(ctx);
|
|
107
|
+
},
|
|
108
|
+
onPostToolUseFailure: async (ctx) => {
|
|
109
|
+
const hooks = subagentHooksMap.get(resolveSubagentName(ctx.args));
|
|
110
|
+
return hooks?.onExecutionFailure?.(ctx) ?? {};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
197
113
|
}
|
|
198
|
-
}
|
|
114
|
+
});
|
|
199
115
|
}
|
|
200
116
|
async function processToolCall(toolCall, turn, handlerContext) {
|
|
201
117
|
const startTime = Date.now();
|
|
118
|
+
const tool = toolMap.get(toolCall.name);
|
|
119
|
+
const toolHooks = tool?.hooks;
|
|
202
120
|
let effectiveArgs = toolCall.args;
|
|
203
121
|
if (options.hooks?.onPreToolUse) {
|
|
204
122
|
const preResult = await options.hooks.onPreToolUse({
|
|
@@ -210,6 +128,7 @@ function createToolRouter(options) {
|
|
|
210
128
|
await appendToolResult({
|
|
211
129
|
threadId: options.threadId,
|
|
212
130
|
toolCallId: toolCall.id,
|
|
131
|
+
toolName: toolCall.name,
|
|
213
132
|
content: JSON.stringify({
|
|
214
133
|
skipped: true,
|
|
215
134
|
reason: "Skipped by PreToolUse hook"
|
|
@@ -221,54 +140,115 @@ function createToolRouter(options) {
|
|
|
221
140
|
effectiveArgs = preResult.modifiedArgs;
|
|
222
141
|
}
|
|
223
142
|
}
|
|
224
|
-
|
|
143
|
+
if (toolHooks?.onPreToolUse) {
|
|
144
|
+
const preResult = await toolHooks.onPreToolUse({
|
|
145
|
+
args: effectiveArgs,
|
|
146
|
+
threadId: options.threadId,
|
|
147
|
+
turn
|
|
148
|
+
});
|
|
149
|
+
if (preResult?.skip) {
|
|
150
|
+
await appendToolResult({
|
|
151
|
+
threadId: options.threadId,
|
|
152
|
+
toolCallId: toolCall.id,
|
|
153
|
+
toolName: toolCall.name,
|
|
154
|
+
content: JSON.stringify({
|
|
155
|
+
skipped: true,
|
|
156
|
+
reason: "Skipped by tool PreToolUse hook"
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (preResult?.modifiedArgs !== void 0) {
|
|
162
|
+
effectiveArgs = preResult.modifiedArgs;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
225
165
|
let result;
|
|
226
166
|
let content;
|
|
167
|
+
let resultAppended = false;
|
|
227
168
|
try {
|
|
228
169
|
if (tool) {
|
|
170
|
+
const enrichedContext = {
|
|
171
|
+
...handlerContext ?? {},
|
|
172
|
+
threadId: options.threadId,
|
|
173
|
+
toolCallId: toolCall.id,
|
|
174
|
+
toolName: toolCall.name
|
|
175
|
+
};
|
|
229
176
|
const response = await tool.handler(
|
|
230
177
|
effectiveArgs,
|
|
231
|
-
|
|
178
|
+
enrichedContext
|
|
232
179
|
);
|
|
233
|
-
result = response.
|
|
234
|
-
content = response.
|
|
180
|
+
result = response.data;
|
|
181
|
+
content = response.toolResponse;
|
|
182
|
+
resultAppended = response.resultAppended === true;
|
|
235
183
|
} else {
|
|
236
184
|
result = { error: `Unknown tool: ${toolCall.name}` };
|
|
237
185
|
content = JSON.stringify(result, null, 2);
|
|
238
186
|
}
|
|
239
187
|
} catch (error) {
|
|
240
|
-
|
|
188
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
189
|
+
let recovered = false;
|
|
190
|
+
if (toolHooks?.onPostToolUseFailure) {
|
|
191
|
+
const failureResult = await toolHooks.onPostToolUseFailure({
|
|
192
|
+
args: effectiveArgs,
|
|
193
|
+
error: err,
|
|
194
|
+
threadId: options.threadId,
|
|
195
|
+
turn
|
|
196
|
+
});
|
|
197
|
+
if (failureResult?.fallbackContent !== void 0) {
|
|
198
|
+
content = failureResult.fallbackContent;
|
|
199
|
+
result = { error: String(error), recovered: true };
|
|
200
|
+
recovered = true;
|
|
201
|
+
} else if (failureResult?.suppress) {
|
|
202
|
+
content = JSON.stringify({ error: String(error), suppressed: true });
|
|
203
|
+
result = { error: String(error), suppressed: true };
|
|
204
|
+
recovered = true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (!recovered && options.hooks?.onPostToolUseFailure) {
|
|
241
208
|
const failureResult = await options.hooks.onPostToolUseFailure({
|
|
242
209
|
toolCall,
|
|
243
|
-
error:
|
|
210
|
+
error: err,
|
|
244
211
|
threadId: options.threadId,
|
|
245
212
|
turn
|
|
246
213
|
});
|
|
247
214
|
if (failureResult?.fallbackContent !== void 0) {
|
|
248
215
|
content = failureResult.fallbackContent;
|
|
249
216
|
result = { error: String(error), recovered: true };
|
|
217
|
+
recovered = true;
|
|
250
218
|
} else if (failureResult?.suppress) {
|
|
251
219
|
content = JSON.stringify({ error: String(error), suppressed: true });
|
|
252
220
|
result = { error: String(error), suppressed: true };
|
|
253
|
-
|
|
254
|
-
throw error;
|
|
221
|
+
recovered = true;
|
|
255
222
|
}
|
|
256
|
-
}
|
|
223
|
+
}
|
|
224
|
+
if (!recovered) {
|
|
257
225
|
throw error;
|
|
258
226
|
}
|
|
259
227
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
228
|
+
if (!resultAppended) {
|
|
229
|
+
await appendToolResult({
|
|
230
|
+
threadId: options.threadId,
|
|
231
|
+
toolCallId: toolCall.id,
|
|
232
|
+
toolName: toolCall.name,
|
|
233
|
+
content
|
|
234
|
+
});
|
|
235
|
+
}
|
|
265
236
|
const toolResult = {
|
|
266
237
|
toolCallId: toolCall.id,
|
|
267
238
|
name: toolCall.name,
|
|
268
|
-
result
|
|
239
|
+
data: result
|
|
269
240
|
};
|
|
241
|
+
const durationMs = Date.now() - startTime;
|
|
242
|
+
if (toolHooks?.onPostToolUse) {
|
|
243
|
+
await toolHooks.onPostToolUse({
|
|
244
|
+
args: effectiveArgs,
|
|
245
|
+
result,
|
|
246
|
+
threadId: options.threadId,
|
|
247
|
+
turn,
|
|
248
|
+
durationMs
|
|
249
|
+
});
|
|
250
|
+
}
|
|
270
251
|
if (options.hooks?.onPostToolUse) {
|
|
271
|
-
const durationMs = Date.now() - startTime;
|
|
272
252
|
await options.hooks.onPostToolUse({
|
|
273
253
|
toolCall,
|
|
274
254
|
result: toolResult,
|
|
@@ -282,11 +262,11 @@ function createToolRouter(options) {
|
|
|
282
262
|
return {
|
|
283
263
|
// --- Methods from registry ---
|
|
284
264
|
hasTools() {
|
|
285
|
-
return toolMap.
|
|
265
|
+
return Array.from(toolMap.values()).some(isEnabled);
|
|
286
266
|
},
|
|
287
267
|
parseToolCall(toolCall) {
|
|
288
268
|
const tool = toolMap.get(toolCall.name);
|
|
289
|
-
if (!tool) {
|
|
269
|
+
if (!tool || !isEnabled(tool)) {
|
|
290
270
|
throw new Error(`Tool ${toolCall.name} not found`);
|
|
291
271
|
}
|
|
292
272
|
const parsedArgs = tool.schema.parse(toolCall.args);
|
|
@@ -297,13 +277,14 @@ function createToolRouter(options) {
|
|
|
297
277
|
};
|
|
298
278
|
},
|
|
299
279
|
hasTool(name) {
|
|
300
|
-
|
|
280
|
+
const tool = toolMap.get(name);
|
|
281
|
+
return tool !== void 0 && isEnabled(tool);
|
|
301
282
|
},
|
|
302
283
|
getToolNames() {
|
|
303
|
-
return Array.from(toolMap.
|
|
284
|
+
return Array.from(toolMap.entries()).filter(([, tool]) => isEnabled(tool)).map(([name]) => name);
|
|
304
285
|
},
|
|
305
286
|
getToolDefinitions() {
|
|
306
|
-
return Array.from(toolMap).map(([name, tool]) => ({
|
|
287
|
+
return Array.from(toolMap).filter(([, tool]) => isEnabled(tool)).map(([name, tool]) => ({
|
|
307
288
|
name,
|
|
308
289
|
description: tool.description,
|
|
309
290
|
schema: tool.schema,
|
|
@@ -342,19 +323,28 @@ function createToolRouter(options) {
|
|
|
342
323
|
}
|
|
343
324
|
const handlerContext = context?.handlerContext ?? {};
|
|
344
325
|
const processOne = async (toolCall) => {
|
|
326
|
+
const enrichedContext = {
|
|
327
|
+
...handlerContext ?? {},
|
|
328
|
+
threadId: options.threadId,
|
|
329
|
+
toolCallId: toolCall.id,
|
|
330
|
+
toolName: toolCall.name
|
|
331
|
+
};
|
|
345
332
|
const response = await handler(
|
|
346
333
|
toolCall.args,
|
|
347
|
-
|
|
334
|
+
enrichedContext
|
|
348
335
|
);
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
336
|
+
if (!response.resultAppended) {
|
|
337
|
+
await appendToolResult({
|
|
338
|
+
threadId: options.threadId,
|
|
339
|
+
toolCallId: toolCall.id,
|
|
340
|
+
toolName: toolCall.name,
|
|
341
|
+
content: response.toolResponse
|
|
342
|
+
});
|
|
343
|
+
}
|
|
354
344
|
return {
|
|
355
345
|
toolCallId: toolCall.id,
|
|
356
346
|
name: toolCall.name,
|
|
357
|
-
|
|
347
|
+
data: response.data
|
|
358
348
|
};
|
|
359
349
|
};
|
|
360
350
|
if (options.parallel) {
|
|
@@ -380,56 +370,50 @@ function createToolRouter(options) {
|
|
|
380
370
|
}
|
|
381
371
|
};
|
|
382
372
|
}
|
|
373
|
+
function withAutoAppend(threadHandler, handler) {
|
|
374
|
+
return async (args, context) => {
|
|
375
|
+
const response = await handler(args, context);
|
|
376
|
+
const threadId = context.threadId;
|
|
377
|
+
const toolCallId = context.toolCallId;
|
|
378
|
+
const toolName = context.toolName;
|
|
379
|
+
await threadHandler({
|
|
380
|
+
threadId,
|
|
381
|
+
toolCallId,
|
|
382
|
+
toolName,
|
|
383
|
+
content: response.toolResponse
|
|
384
|
+
});
|
|
385
|
+
return { toolResponse: "", data: response.data, resultAppended: true };
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function defineTool(tool) {
|
|
389
|
+
return tool;
|
|
390
|
+
}
|
|
391
|
+
function defineSubagent(config) {
|
|
392
|
+
return config;
|
|
393
|
+
}
|
|
383
394
|
function hasNoOtherToolCalls(toolCalls, excludeName) {
|
|
384
395
|
return toolCalls.filter((tc) => tc.name !== excludeName).length === 0;
|
|
385
396
|
}
|
|
386
397
|
|
|
387
398
|
// src/lib/session.ts
|
|
388
|
-
async function resolvePrompt(prompt) {
|
|
389
|
-
if (typeof prompt === "function") {
|
|
390
|
-
return prompt();
|
|
391
|
-
}
|
|
392
|
-
return prompt;
|
|
393
|
-
}
|
|
394
399
|
var createSession = async ({
|
|
395
400
|
threadId,
|
|
396
401
|
agentName,
|
|
397
402
|
maxTurns = 50,
|
|
398
403
|
metadata = {},
|
|
399
404
|
runAgent,
|
|
400
|
-
|
|
401
|
-
instructionsPrompt,
|
|
405
|
+
threadOps,
|
|
402
406
|
buildContextMessage,
|
|
403
|
-
buildFileTree = async () => "",
|
|
404
407
|
subagents,
|
|
405
408
|
tools = {},
|
|
406
409
|
processToolsInParallel = true,
|
|
407
|
-
buildInTools = {},
|
|
408
410
|
hooks = {}
|
|
409
411
|
}) => {
|
|
410
|
-
const {
|
|
411
|
-
initializeThread,
|
|
412
|
-
appendHumanMessage,
|
|
413
|
-
parseToolCalls,
|
|
414
|
-
appendToolResult,
|
|
415
|
-
appendSystemMessage
|
|
416
|
-
} = proxyActivities({
|
|
417
|
-
startToCloseTimeout: "30m",
|
|
418
|
-
retry: {
|
|
419
|
-
maximumAttempts: 6,
|
|
420
|
-
initialInterval: "5s",
|
|
421
|
-
maximumInterval: "15m",
|
|
422
|
-
backoffCoefficient: 4
|
|
423
|
-
},
|
|
424
|
-
heartbeatTimeout: "5m"
|
|
425
|
-
});
|
|
426
|
-
const fileTree = await buildFileTree();
|
|
427
412
|
const toolRouter = createToolRouter({
|
|
428
413
|
tools,
|
|
414
|
+
appendToolResult: threadOps.appendToolResult,
|
|
429
415
|
threadId,
|
|
430
416
|
hooks,
|
|
431
|
-
buildInTools,
|
|
432
|
-
fileTree,
|
|
433
417
|
subagents,
|
|
434
418
|
parallel: processToolsInParallel
|
|
435
419
|
});
|
|
@@ -454,15 +438,8 @@ var createSession = async ({
|
|
|
454
438
|
});
|
|
455
439
|
}
|
|
456
440
|
stateManager.setTools(toolRouter.getToolDefinitions());
|
|
457
|
-
await initializeThread(threadId);
|
|
458
|
-
await
|
|
459
|
-
threadId,
|
|
460
|
-
[
|
|
461
|
-
await resolvePrompt(baseSystemPrompt),
|
|
462
|
-
await resolvePrompt(instructionsPrompt)
|
|
463
|
-
].join("\n")
|
|
464
|
-
);
|
|
465
|
-
await appendHumanMessage(threadId, await buildContextMessage());
|
|
441
|
+
await threadOps.initializeThread(threadId);
|
|
442
|
+
await threadOps.appendHumanMessage(threadId, await buildContextMessage());
|
|
466
443
|
let exitReason = "completed";
|
|
467
444
|
try {
|
|
468
445
|
while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
|
|
@@ -483,24 +460,25 @@ var createSession = async ({
|
|
|
483
460
|
exitReason = "completed";
|
|
484
461
|
return message;
|
|
485
462
|
}
|
|
486
|
-
const rawToolCalls = await parseToolCalls(message);
|
|
487
|
-
const parsedToolCalls =
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
tc
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
{
|
|
501
|
-
turn: currentTurn
|
|
463
|
+
const rawToolCalls = await threadOps.parseToolCalls(message);
|
|
464
|
+
const parsedToolCalls = [];
|
|
465
|
+
for (const tc of rawToolCalls) {
|
|
466
|
+
try {
|
|
467
|
+
parsedToolCalls.push(toolRouter.parseToolCall(tc));
|
|
468
|
+
} catch (error) {
|
|
469
|
+
await threadOps.appendToolResult({
|
|
470
|
+
threadId,
|
|
471
|
+
toolCallId: tc.id ?? "",
|
|
472
|
+
toolName: tc.name,
|
|
473
|
+
content: JSON.stringify({
|
|
474
|
+
error: `Invalid tool call for "${tc.name}": ${error instanceof Error ? error.message : String(error)}`
|
|
475
|
+
})
|
|
476
|
+
});
|
|
502
477
|
}
|
|
503
|
-
|
|
478
|
+
}
|
|
479
|
+
await toolRouter.processToolCalls(parsedToolCalls, {
|
|
480
|
+
turn: currentTurn
|
|
481
|
+
});
|
|
504
482
|
if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
|
|
505
483
|
exitReason = "waiting_for_input";
|
|
506
484
|
break;
|
|
@@ -519,13 +497,31 @@ var createSession = async ({
|
|
|
519
497
|
}
|
|
520
498
|
};
|
|
521
499
|
};
|
|
500
|
+
function proxyDefaultThreadOps(options) {
|
|
501
|
+
const activities = proxyActivities(
|
|
502
|
+
options ?? {
|
|
503
|
+
startToCloseTimeout: "30m",
|
|
504
|
+
retry: {
|
|
505
|
+
maximumAttempts: 6,
|
|
506
|
+
initialInterval: "5s",
|
|
507
|
+
maximumInterval: "15m",
|
|
508
|
+
backoffCoefficient: 4
|
|
509
|
+
},
|
|
510
|
+
heartbeatTimeout: "5m"
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
return {
|
|
514
|
+
initializeThread: activities.initializeThread,
|
|
515
|
+
appendHumanMessage: activities.appendHumanMessage,
|
|
516
|
+
appendToolResult: activities.appendToolResult,
|
|
517
|
+
parseToolCalls: activities.parseToolCalls
|
|
518
|
+
};
|
|
519
|
+
}
|
|
522
520
|
|
|
523
521
|
// src/lib/types.ts
|
|
524
522
|
function isTerminalStatus(status) {
|
|
525
523
|
return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
|
|
526
524
|
}
|
|
527
|
-
|
|
528
|
-
// src/lib/state-manager.ts
|
|
529
525
|
var getStateQuery = defineQuery("getState");
|
|
530
526
|
function createAgentStateManager(initialState) {
|
|
531
527
|
let status = initialState?.status ?? "RUNNING";
|
|
@@ -620,7 +616,13 @@ function createAgentStateManager(initialState) {
|
|
|
620
616
|
version++;
|
|
621
617
|
},
|
|
622
618
|
setTools(newTools) {
|
|
623
|
-
tools = newTools
|
|
619
|
+
tools = newTools.map((tool) => ({
|
|
620
|
+
name: tool.name,
|
|
621
|
+
description: tool.description,
|
|
622
|
+
schema: z.toJSONSchema(tool.schema),
|
|
623
|
+
strict: tool.strict,
|
|
624
|
+
max_uses: tool.max_uses
|
|
625
|
+
}));
|
|
624
626
|
},
|
|
625
627
|
deleteTask(id) {
|
|
626
628
|
const deleted = tasks.delete(id);
|
|
@@ -651,18 +653,18 @@ Usage notes:
|
|
|
651
653
|
* Use multiSelect: true to allow multiple answers to be selected for a question
|
|
652
654
|
* If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
|
|
653
655
|
`,
|
|
654
|
-
schema:
|
|
655
|
-
questions:
|
|
656
|
-
|
|
657
|
-
question:
|
|
658
|
-
header:
|
|
659
|
-
options:
|
|
660
|
-
|
|
661
|
-
label:
|
|
662
|
-
description:
|
|
656
|
+
schema: z3.object({
|
|
657
|
+
questions: z3.array(
|
|
658
|
+
z3.object({
|
|
659
|
+
question: z3.string().describe("The full question text to display"),
|
|
660
|
+
header: z3.string().describe("Short label for the question (max 12 characters)"),
|
|
661
|
+
options: z3.array(
|
|
662
|
+
z3.object({
|
|
663
|
+
label: z3.string(),
|
|
664
|
+
description: z3.string()
|
|
663
665
|
})
|
|
664
666
|
).min(0).max(4).describe("Array of 0-4 choices, each with label and description"),
|
|
665
|
-
multiSelect:
|
|
667
|
+
multiSelect: z3.boolean().describe("If true, users can select multiple options")
|
|
666
668
|
})
|
|
667
669
|
)
|
|
668
670
|
}),
|
|
@@ -740,7 +742,7 @@ var writeTool = {
|
|
|
740
742
|
description: `Create or overwrite a file with new content.
|
|
741
743
|
|
|
742
744
|
Usage:
|
|
743
|
-
- Provide the absolute
|
|
745
|
+
- Provide the absolute path to the file
|
|
744
746
|
- The file will be created if it doesn't exist
|
|
745
747
|
- If the file exists, it will be completely overwritten
|
|
746
748
|
|
|
@@ -750,7 +752,7 @@ IMPORTANT:
|
|
|
750
752
|
- Path must be absolute (e.g., "/docs/readme.md", not "docs/readme.md")
|
|
751
753
|
`,
|
|
752
754
|
schema: z.object({
|
|
753
|
-
file_path: z.string().describe("The absolute
|
|
755
|
+
file_path: z.string().describe("The absolute path to the file to write"),
|
|
754
756
|
content: z.string().describe("The content to write to the file")
|
|
755
757
|
}),
|
|
756
758
|
strict: true
|
|
@@ -783,15 +785,64 @@ IMPORTANT:
|
|
|
783
785
|
}),
|
|
784
786
|
strict: true
|
|
785
787
|
};
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
788
|
+
var taskCreateTool = {
|
|
789
|
+
name: "TaskCreate",
|
|
790
|
+
description: `Use this tool to create a structured task list for the control test. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
|
|
791
|
+
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
792
|
+
|
|
793
|
+
## When to Use This Tool
|
|
794
|
+
|
|
795
|
+
Use this tool proactively in these scenarios:
|
|
796
|
+
|
|
797
|
+
- Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
798
|
+
- Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
799
|
+
- User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
800
|
+
- User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
801
|
+
- After receiving new instructions - Immediately capture user requirements as tasks
|
|
802
|
+
- When you start working on a task - Mark it as in_progress BEFORE beginning work
|
|
803
|
+
- After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
|
|
804
|
+
|
|
805
|
+
## When NOT to Use This Tool
|
|
806
|
+
|
|
807
|
+
Skip using this tool when:
|
|
808
|
+
- There is only a single, straightforward task
|
|
809
|
+
- The task is trivial and tracking it provides no organizational benefit
|
|
810
|
+
- The task can be completed in less than 3 trivial steps
|
|
811
|
+
- The task is purely conversational or informational
|
|
812
|
+
|
|
813
|
+
NOTE that you should not use this tool if there is only one trivial task to do. In this case you are better off just doing the task directly.
|
|
814
|
+
|
|
815
|
+
## Task Fields
|
|
816
|
+
|
|
817
|
+
- **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
|
|
818
|
+
- **description**: Detailed description of what needs to be done, including context and acceptance criteria
|
|
819
|
+
- **activeForm**: Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.
|
|
820
|
+
|
|
821
|
+
**IMPORTANT**: Always provide activeForm when creating tasks. The subject should be imperative ("Run tests") while activeForm should be present continuous ("Running tests"). All tasks are created with status \`pending\`.
|
|
822
|
+
|
|
823
|
+
## Tips
|
|
824
|
+
|
|
825
|
+
- Create tasks with clear, specific subjects that describe the outcome
|
|
826
|
+
- Include enough detail in the description for another agent to understand and complete the task
|
|
827
|
+
- After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
|
|
828
|
+
- Check TaskList first to avoid creating duplicate tasks`,
|
|
829
|
+
schema: z3.object({
|
|
830
|
+
subject: z3.string().describe(
|
|
831
|
+
'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
|
|
832
|
+
),
|
|
833
|
+
description: z3.string().describe(
|
|
834
|
+
"Detailed description of what needs to be done, including context and acceptance criteria"
|
|
835
|
+
),
|
|
836
|
+
activeForm: z3.string().describe(
|
|
837
|
+
'Present continuous form shown in spinner when task is in_progress (e.g., "Fixing authentication bug"). This is displayed to the user while you work on the task.'
|
|
838
|
+
),
|
|
839
|
+
metadata: z3.record(z3.string(), z3.string()).describe("Arbitrary key-value pairs for tracking")
|
|
840
|
+
})
|
|
841
|
+
};
|
|
842
|
+
function createTaskCreateHandler(stateManager) {
|
|
792
843
|
return (args) => {
|
|
793
844
|
const task = {
|
|
794
|
-
id:
|
|
845
|
+
id: uuid4(),
|
|
795
846
|
subject: args.subject,
|
|
796
847
|
description: args.description,
|
|
797
848
|
activeForm: args.activeForm,
|
|
@@ -802,16 +853,16 @@ function createTaskCreateHandler({
|
|
|
802
853
|
};
|
|
803
854
|
stateManager.setTask(task);
|
|
804
855
|
return {
|
|
805
|
-
|
|
806
|
-
|
|
856
|
+
toolResponse: JSON.stringify(task, null, 2),
|
|
857
|
+
data: task
|
|
807
858
|
};
|
|
808
859
|
};
|
|
809
860
|
}
|
|
810
861
|
var taskGetTool = {
|
|
811
862
|
name: "TaskGet",
|
|
812
863
|
description: `Retrieve full task details including dependencies.`,
|
|
813
|
-
schema:
|
|
814
|
-
taskId:
|
|
864
|
+
schema: z3.object({
|
|
865
|
+
taskId: z3.string().describe("The ID of the task to get")
|
|
815
866
|
})
|
|
816
867
|
};
|
|
817
868
|
|
|
@@ -821,40 +872,40 @@ function createTaskGetHandler(stateManager) {
|
|
|
821
872
|
const task = stateManager.getTask(args.taskId) ?? null;
|
|
822
873
|
if (!task) {
|
|
823
874
|
return {
|
|
824
|
-
|
|
825
|
-
|
|
875
|
+
toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
|
|
876
|
+
data: null
|
|
826
877
|
};
|
|
827
878
|
}
|
|
828
879
|
return {
|
|
829
|
-
|
|
830
|
-
|
|
880
|
+
toolResponse: JSON.stringify(task, null, 2),
|
|
881
|
+
data: task
|
|
831
882
|
};
|
|
832
883
|
};
|
|
833
884
|
}
|
|
834
885
|
var taskListTool = {
|
|
835
886
|
name: "TaskList",
|
|
836
887
|
description: `List all tasks with current state.`,
|
|
837
|
-
schema:
|
|
888
|
+
schema: z3.object({})
|
|
838
889
|
};
|
|
839
890
|
|
|
840
891
|
// src/tools/task-list/handler.ts
|
|
841
892
|
function createTaskListHandler(stateManager) {
|
|
842
|
-
return (
|
|
893
|
+
return () => {
|
|
843
894
|
const taskList = stateManager.getTasks();
|
|
844
895
|
return {
|
|
845
|
-
|
|
846
|
-
|
|
896
|
+
toolResponse: JSON.stringify(taskList, null, 2),
|
|
897
|
+
data: taskList
|
|
847
898
|
};
|
|
848
899
|
};
|
|
849
900
|
}
|
|
850
901
|
var taskUpdateTool = {
|
|
851
902
|
name: "TaskUpdate",
|
|
852
903
|
description: `Update status, add blockers, modify details.`,
|
|
853
|
-
schema:
|
|
854
|
-
taskId:
|
|
855
|
-
status:
|
|
856
|
-
addBlockedBy:
|
|
857
|
-
addBlocks:
|
|
904
|
+
schema: z3.object({
|
|
905
|
+
taskId: z3.string().describe("The ID of the task to get"),
|
|
906
|
+
status: z3.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
|
|
907
|
+
addBlockedBy: z3.array(z3.string()).describe("The IDs of the tasks that are blocking this task"),
|
|
908
|
+
addBlocks: z3.array(z3.string()).describe("The IDs of the tasks that this task is blocking")
|
|
858
909
|
})
|
|
859
910
|
};
|
|
860
911
|
|
|
@@ -864,8 +915,8 @@ function createTaskUpdateHandler(stateManager) {
|
|
|
864
915
|
const task = stateManager.getTask(args.taskId);
|
|
865
916
|
if (!task) {
|
|
866
917
|
return {
|
|
867
|
-
|
|
868
|
-
|
|
918
|
+
toolResponse: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
|
|
919
|
+
data: null
|
|
869
920
|
};
|
|
870
921
|
}
|
|
871
922
|
if (args.status) {
|
|
@@ -897,11 +948,38 @@ function createTaskUpdateHandler(stateManager) {
|
|
|
897
948
|
}
|
|
898
949
|
stateManager.setTask(task);
|
|
899
950
|
return {
|
|
900
|
-
|
|
901
|
-
|
|
951
|
+
toolResponse: JSON.stringify(task, null, 2),
|
|
952
|
+
data: task
|
|
902
953
|
};
|
|
903
954
|
};
|
|
904
955
|
}
|
|
956
|
+
var createBashToolDescription = ({
|
|
957
|
+
fileTree
|
|
958
|
+
}) => `Execute shell commands in a bash environment.
|
|
959
|
+
|
|
960
|
+
Use this tool to:
|
|
961
|
+
- Run shell commands (ls, cat, grep, find, etc.)
|
|
962
|
+
- Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
|
|
963
|
+
- Inspect files and directories
|
|
964
|
+
|
|
965
|
+
Current file tree:
|
|
966
|
+
${fileTree}`;
|
|
967
|
+
var bashTool = {
|
|
968
|
+
name: "Bash",
|
|
969
|
+
description: `Execute shell commands in a sandboxed bash environment.
|
|
970
|
+
|
|
971
|
+
Use this tool to:
|
|
972
|
+
- Run shell commands (ls, cat, grep, find, etc.)
|
|
973
|
+
- Execute scripts and chain commands with pipes (|) or logical operators (&&, ||)
|
|
974
|
+
- Inspect files and directories
|
|
975
|
+
`,
|
|
976
|
+
schema: z3.object({
|
|
977
|
+
command: z3.string().describe(
|
|
978
|
+
"The bash command to execute. Can include pipes (|), redirects (>, >>), logical operators (&&, ||), and shell features like command substitution $(...)."
|
|
979
|
+
)
|
|
980
|
+
}),
|
|
981
|
+
strict: true
|
|
982
|
+
};
|
|
905
983
|
|
|
906
984
|
// node_modules/uuid/dist/esm-node/stringify.js
|
|
907
985
|
var byteToHex = [];
|
|
@@ -950,25 +1028,33 @@ function getThreadKey(threadId, key) {
|
|
|
950
1028
|
return `thread:${threadId}:${key}`;
|
|
951
1029
|
}
|
|
952
1030
|
function createThreadManager(config) {
|
|
953
|
-
const {
|
|
1031
|
+
const {
|
|
1032
|
+
redis,
|
|
1033
|
+
threadId,
|
|
1034
|
+
key = "messages",
|
|
1035
|
+
serialize = (m) => JSON.stringify(m),
|
|
1036
|
+
deserialize = (raw) => JSON.parse(raw)
|
|
1037
|
+
} = config;
|
|
954
1038
|
const redisKey = getThreadKey(threadId, key);
|
|
955
|
-
|
|
1039
|
+
const base = {
|
|
956
1040
|
async initialize() {
|
|
957
1041
|
await redis.del(redisKey);
|
|
958
1042
|
},
|
|
959
1043
|
async load() {
|
|
960
1044
|
const data = await redis.lrange(redisKey, 0, -1);
|
|
961
|
-
return data.map(
|
|
1045
|
+
return data.map(deserialize);
|
|
962
1046
|
},
|
|
963
1047
|
async append(messages) {
|
|
964
1048
|
if (messages.length > 0) {
|
|
965
|
-
await redis.rpush(redisKey, ...messages.map(
|
|
1049
|
+
await redis.rpush(redisKey, ...messages.map(serialize));
|
|
966
1050
|
await redis.expire(redisKey, THREAD_TTL_SECONDS);
|
|
967
1051
|
}
|
|
968
1052
|
},
|
|
969
1053
|
async delete() {
|
|
970
1054
|
await redis.del(redisKey);
|
|
971
|
-
}
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
const helpers = {
|
|
972
1058
|
createHumanMessage(content) {
|
|
973
1059
|
return new HumanMessage({
|
|
974
1060
|
id: v4_default(),
|
|
@@ -988,40 +1074,27 @@ function createThreadManager(config) {
|
|
|
988
1074
|
},
|
|
989
1075
|
createToolMessage(content, toolCallId) {
|
|
990
1076
|
return new ToolMessage({
|
|
991
|
-
// Cast needed due to langchain type compatibility
|
|
992
1077
|
content,
|
|
993
1078
|
tool_call_id: toolCallId
|
|
994
1079
|
}).toDict();
|
|
995
1080
|
},
|
|
996
|
-
createSystemMessage(content) {
|
|
997
|
-
return new SystemMessage({
|
|
998
|
-
content
|
|
999
|
-
}).toDict();
|
|
1000
|
-
},
|
|
1001
|
-
async appendSystemMessage(content) {
|
|
1002
|
-
const message = this.createSystemMessage(content);
|
|
1003
|
-
await this.append([message]);
|
|
1004
|
-
},
|
|
1005
1081
|
async appendHumanMessage(content) {
|
|
1006
|
-
const message =
|
|
1007
|
-
await
|
|
1082
|
+
const message = helpers.createHumanMessage(content);
|
|
1083
|
+
await base.append([message]);
|
|
1008
1084
|
},
|
|
1009
1085
|
async appendToolMessage(content, toolCallId) {
|
|
1010
|
-
const message =
|
|
1011
|
-
await
|
|
1086
|
+
const message = helpers.createToolMessage(content, toolCallId);
|
|
1087
|
+
await base.append([message]);
|
|
1012
1088
|
},
|
|
1013
1089
|
async appendAIMessage(content) {
|
|
1014
|
-
const message =
|
|
1015
|
-
await
|
|
1090
|
+
const message = helpers.createAIMessage(content);
|
|
1091
|
+
await base.append([message]);
|
|
1016
1092
|
}
|
|
1017
1093
|
};
|
|
1094
|
+
return Object.assign(base, helpers);
|
|
1018
1095
|
}
|
|
1019
1096
|
function createSharedActivities(redis) {
|
|
1020
1097
|
return {
|
|
1021
|
-
async appendSystemMessage(threadId, content) {
|
|
1022
|
-
const thread = createThreadManager({ redis, threadId });
|
|
1023
|
-
await thread.appendSystemMessage(content);
|
|
1024
|
-
},
|
|
1025
1098
|
async appendToolResult(config) {
|
|
1026
1099
|
const { threadId, toolCallId, content } = config;
|
|
1027
1100
|
const thread = createThreadManager({ redis, threadId });
|
|
@@ -1094,7 +1167,7 @@ async function invokeModel({
|
|
|
1094
1167
|
}
|
|
1095
1168
|
};
|
|
1096
1169
|
}
|
|
1097
|
-
var
|
|
1170
|
+
var createAskUserQuestionHandler = () => async (args) => {
|
|
1098
1171
|
const messages = args.questions.map(
|
|
1099
1172
|
({ question, header, options, multiSelect }) => new AIMessage({
|
|
1100
1173
|
content: question,
|
|
@@ -1105,101 +1178,81 @@ var handleAskUserQuestionToolResult = async (args) => {
|
|
|
1105
1178
|
}
|
|
1106
1179
|
}).toDict()
|
|
1107
1180
|
);
|
|
1108
|
-
return {
|
|
1181
|
+
return { toolResponse: "Question submitted", data: { chatMessages: messages } };
|
|
1109
1182
|
};
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1183
|
+
function createGlobHandler(fs) {
|
|
1184
|
+
return async (_args) => {
|
|
1185
|
+
new Bash({ fs });
|
|
1186
|
+
return {
|
|
1187
|
+
toolResponse: "Hello, world!",
|
|
1188
|
+
data: { files: [] }
|
|
1189
|
+
};
|
|
1190
|
+
};
|
|
1116
1191
|
}
|
|
1117
1192
|
|
|
1118
1193
|
// src/tools/edit/handler.ts
|
|
1119
1194
|
function escapeRegExp(str) {
|
|
1120
1195
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1121
1196
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
content: `Error: old_string and new_string must be different.`,
|
|
1127
|
-
result: {
|
|
1128
|
-
path: file_path,
|
|
1129
|
-
success: false,
|
|
1130
|
-
replacements: 0
|
|
1131
|
-
}
|
|
1132
|
-
};
|
|
1133
|
-
}
|
|
1134
|
-
try {
|
|
1135
|
-
const exists = await fs.exists(file_path);
|
|
1136
|
-
if (!exists) {
|
|
1197
|
+
function createEditHandler(fs) {
|
|
1198
|
+
return async (args) => {
|
|
1199
|
+
const { file_path, old_string, new_string, replace_all = false } = args;
|
|
1200
|
+
if (old_string === new_string) {
|
|
1137
1201
|
return {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
path: file_path,
|
|
1141
|
-
success: false,
|
|
1142
|
-
replacements: 0
|
|
1143
|
-
}
|
|
1202
|
+
toolResponse: `Error: old_string and new_string must be different.`,
|
|
1203
|
+
data: { path: file_path, success: false, replacements: 0 }
|
|
1144
1204
|
};
|
|
1145
1205
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1206
|
+
try {
|
|
1207
|
+
const exists = await fs.exists(file_path);
|
|
1208
|
+
if (!exists) {
|
|
1209
|
+
return {
|
|
1210
|
+
toolResponse: `Error: File "${file_path}" does not exist.`,
|
|
1211
|
+
data: { path: file_path, success: false, replacements: 0 }
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
const content = await fs.readFile(file_path);
|
|
1215
|
+
if (!content.includes(old_string)) {
|
|
1216
|
+
return {
|
|
1217
|
+
toolResponse: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
|
|
1218
|
+
data: { path: file_path, success: false, replacements: 0 }
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
const escapedOldString = escapeRegExp(old_string);
|
|
1222
|
+
const globalRegex = new RegExp(escapedOldString, "g");
|
|
1223
|
+
const occurrences = (content.match(globalRegex) || []).length;
|
|
1224
|
+
if (!replace_all && occurrences > 1) {
|
|
1225
|
+
return {
|
|
1226
|
+
toolResponse: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
|
|
1227
|
+
data: { path: file_path, success: false, replacements: 0 }
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
let newContent;
|
|
1231
|
+
let replacements;
|
|
1232
|
+
if (replace_all) {
|
|
1233
|
+
newContent = content.split(old_string).join(new_string);
|
|
1234
|
+
replacements = occurrences;
|
|
1235
|
+
} else {
|
|
1236
|
+
const index = content.indexOf(old_string);
|
|
1237
|
+
newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
|
|
1238
|
+
replacements = 1;
|
|
1239
|
+
}
|
|
1240
|
+
await fs.writeFile(file_path, newContent);
|
|
1241
|
+
const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
|
|
1148
1242
|
return {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
path: file_path,
|
|
1152
|
-
success: false,
|
|
1153
|
-
replacements: 0
|
|
1154
|
-
}
|
|
1243
|
+
toolResponse: `${summary} in ${file_path}`,
|
|
1244
|
+
data: { path: file_path, success: true, replacements }
|
|
1155
1245
|
};
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
const globalRegex = new RegExp(escapedOldString, "g");
|
|
1159
|
-
const occurrences = (content.match(globalRegex) || []).length;
|
|
1160
|
-
if (!replace_all && occurrences > 1) {
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1161
1248
|
return {
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
path: file_path,
|
|
1165
|
-
success: false,
|
|
1166
|
-
replacements: 0
|
|
1167
|
-
}
|
|
1249
|
+
toolResponse: `Error editing file "${file_path}": ${message}`,
|
|
1250
|
+
data: { path: file_path, success: false, replacements: 0 }
|
|
1168
1251
|
};
|
|
1169
1252
|
}
|
|
1170
|
-
|
|
1171
|
-
let replacements;
|
|
1172
|
-
if (replace_all) {
|
|
1173
|
-
newContent = content.split(old_string).join(new_string);
|
|
1174
|
-
replacements = occurrences;
|
|
1175
|
-
} else {
|
|
1176
|
-
const index = content.indexOf(old_string);
|
|
1177
|
-
newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
|
|
1178
|
-
replacements = 1;
|
|
1179
|
-
}
|
|
1180
|
-
await fs.writeFile(file_path, newContent);
|
|
1181
|
-
const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
|
|
1182
|
-
return {
|
|
1183
|
-
content: `${summary} in ${file_path}`,
|
|
1184
|
-
result: {
|
|
1185
|
-
path: file_path,
|
|
1186
|
-
success: true,
|
|
1187
|
-
replacements
|
|
1188
|
-
}
|
|
1189
|
-
};
|
|
1190
|
-
} catch (error) {
|
|
1191
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1192
|
-
return {
|
|
1193
|
-
content: `Error editing file "${file_path}": ${message}`,
|
|
1194
|
-
result: {
|
|
1195
|
-
path: file_path,
|
|
1196
|
-
success: false,
|
|
1197
|
-
replacements: 0
|
|
1198
|
-
}
|
|
1199
|
-
};
|
|
1200
|
-
}
|
|
1253
|
+
};
|
|
1201
1254
|
}
|
|
1202
|
-
var
|
|
1255
|
+
var createBashHandler = (bashOptions) => async (args, _context) => {
|
|
1203
1256
|
const { command } = args;
|
|
1204
1257
|
const mergedOptions = {
|
|
1205
1258
|
...bashOptions,
|
|
@@ -1214,20 +1267,20 @@ var handleBashTool = (bashOptions) => async (args, _context) => {
|
|
|
1214
1267
|
const { exitCode, stderr, stdout } = await bash.exec(command);
|
|
1215
1268
|
const bashExecOut = { exitCode, stderr, stdout };
|
|
1216
1269
|
return {
|
|
1217
|
-
|
|
1270
|
+
toolResponse: `Exit code: ${exitCode}
|
|
1218
1271
|
|
|
1219
1272
|
stdout:
|
|
1220
1273
|
${stdout}
|
|
1221
1274
|
|
|
1222
1275
|
stderr:
|
|
1223
1276
|
${stderr}`,
|
|
1224
|
-
|
|
1277
|
+
data: bashExecOut
|
|
1225
1278
|
};
|
|
1226
1279
|
} catch (error) {
|
|
1227
1280
|
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
1228
1281
|
return {
|
|
1229
|
-
|
|
1230
|
-
|
|
1282
|
+
toolResponse: `Error executing bash command: ${err.message}`,
|
|
1283
|
+
data: null
|
|
1231
1284
|
};
|
|
1232
1285
|
}
|
|
1233
1286
|
};
|
|
@@ -1296,6 +1349,6 @@ var toTree = async (fs, opts = {}) => {
|
|
|
1296
1349
|
return base + subtree;
|
|
1297
1350
|
};
|
|
1298
1351
|
|
|
1299
|
-
export { AGENT_HANDLER_NAMES, ZeitlichPlugin, askUserQuestionTool, bashTool, createAgentStateManager, createSession, createSharedActivities, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskTool, createTaskUpdateHandler, createToolRouter,
|
|
1352
|
+
export { AGENT_HANDLER_NAMES, ZeitlichPlugin, askUserQuestionTool, bashTool, createAgentStateManager, createAskUserQuestionHandler, createBashHandler, createBashToolDescription, createEditHandler, createGlobHandler, createSession, createSharedActivities, createTaskCreateHandler, createTaskGetHandler, createTaskListHandler, createTaskTool, createTaskUpdateHandler, createThreadManager, createToolRouter, defineSubagent, defineTool, editTool, getStateQuery, globTool, grepTool, hasNoOtherToolCalls, invokeModel, isTerminalStatus, proxyDefaultThreadOps, readTool, taskCreateTool, taskGetTool, taskListTool, taskUpdateTool, toTree, withAutoAppend, writeTool };
|
|
1300
1353
|
//# sourceMappingURL=index.js.map
|
|
1301
1354
|
//# sourceMappingURL=index.js.map
|