zeitlich 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -180
- package/dist/index.cjs +1314 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +128 -0
- package/dist/index.d.ts +51 -75
- package/dist/index.js +732 -1077
- package/dist/index.js.map +1 -1
- package/dist/workflow-uVNF7zoe.d.cts +941 -0
- package/dist/workflow-uVNF7zoe.d.ts +941 -0
- package/dist/workflow.cjs +914 -0
- package/dist/workflow.cjs.map +1 -0
- package/dist/workflow.d.cts +5 -0
- package/dist/workflow.d.ts +2 -1
- package/dist/workflow.js +547 -422
- package/dist/workflow.js.map +1 -1
- package/package.json +25 -17
- package/src/activities.ts +112 -0
- package/src/index.ts +49 -0
- package/src/lib/fs.ts +80 -0
- package/src/lib/model-invoker.ts +75 -0
- package/src/lib/session.ts +216 -0
- package/src/lib/state-manager.ts +268 -0
- package/src/lib/thread-manager.ts +169 -0
- package/src/lib/tool-router.ts +717 -0
- package/src/lib/types.ts +354 -0
- package/src/plugin.ts +28 -0
- package/src/tools/ask-user-question/handler.ts +25 -0
- package/src/tools/ask-user-question/tool.ts +46 -0
- package/src/tools/bash/bash.test.ts +104 -0
- package/src/tools/bash/handler.ts +36 -0
- package/src/tools/bash/tool.ts +20 -0
- package/src/tools/edit/handler.ts +156 -0
- package/src/tools/edit/tool.ts +39 -0
- package/src/tools/glob/handler.ts +62 -0
- package/src/tools/glob/tool.ts +27 -0
- package/src/tools/grep/tool.ts +45 -0
- package/src/tools/read/tool.ts +33 -0
- package/src/tools/task/handler.ts +75 -0
- package/src/tools/task/tool.ts +96 -0
- package/src/tools/task-create/handler.ts +49 -0
- package/src/tools/task-create/tool.ts +66 -0
- package/src/tools/task-get/handler.ts +38 -0
- package/src/tools/task-get/tool.ts +11 -0
- package/src/tools/task-list/handler.ts +33 -0
- package/src/tools/task-list/tool.ts +9 -0
- package/src/tools/task-update/handler.ts +79 -0
- package/src/tools/task-update/tool.ts +20 -0
- package/src/tools/write/tool.ts +26 -0
- package/src/workflow.ts +138 -0
- package/tsup.config.ts +20 -0
- package/dist/index.d.mts +0 -152
- package/dist/index.mjs +0 -1582
- package/dist/index.mjs.map +0 -1
- package/dist/workflow-DeVGEXSc.d.mts +0 -1201
- package/dist/workflow-DeVGEXSc.d.ts +0 -1201
- package/dist/workflow.d.mts +0 -4
- package/dist/workflow.mjs +0 -734
- package/dist/workflow.mjs.map +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1314 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var workflow = require('@temporalio/workflow');
|
|
4
|
+
var z4 = require('zod');
|
|
5
|
+
var plugin = require('@temporalio/plugin');
|
|
6
|
+
var messages = require('@langchain/core/messages');
|
|
7
|
+
var crypto = require('crypto');
|
|
8
|
+
var activity = require('@temporalio/activity');
|
|
9
|
+
var justBash = require('just-bash');
|
|
10
|
+
|
|
11
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
|
+
|
|
13
|
+
var z4__default = /*#__PURE__*/_interopDefault(z4);
|
|
14
|
+
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
15
|
+
|
|
16
|
+
// src/lib/session.ts
|
|
17
|
+
var TASK_TOOL = "Task";
|
|
18
|
+
function buildTaskDescription(subagents) {
|
|
19
|
+
const subagentList = subagents.map((s) => `- **${s.name}**: ${s.description}`).join("\n");
|
|
20
|
+
return `Launch a new agent to handle complex, multi-step tasks autonomously.
|
|
21
|
+
|
|
22
|
+
The ${TASK_TOOL} tool launches specialized agents (subprocesses) that autonomously handle complex tasks. Each agent type has specific capabilities and tools available to it.
|
|
23
|
+
|
|
24
|
+
Available agent types:
|
|
25
|
+
|
|
26
|
+
${subagentList}
|
|
27
|
+
|
|
28
|
+
When using the ${TASK_TOOL} tool, you must specify a subagent parameter to select which agent type to use.
|
|
29
|
+
|
|
30
|
+
Usage notes:
|
|
31
|
+
|
|
32
|
+
- Always include a short description (3-5 words) summarizing what the agent will do
|
|
33
|
+
- Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
|
|
34
|
+
- When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
|
|
35
|
+
- Each invocation starts fresh - provide a detailed task description with all necessary context.
|
|
36
|
+
- Provide clear, detailed prompts so the agent can work autonomously and return exactly the information you need.
|
|
37
|
+
- The agent's outputs should generally be trusted
|
|
38
|
+
- Clearly tell the agent what type of work you expect since it is not aware of the user's intent
|
|
39
|
+
- 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.`;
|
|
40
|
+
}
|
|
41
|
+
function createTaskTool(subagents) {
|
|
42
|
+
if (subagents.length === 0) {
|
|
43
|
+
throw new Error("createTaskTool requires at least one subagent");
|
|
44
|
+
}
|
|
45
|
+
const names = subagents.map((s) => s.name);
|
|
46
|
+
return {
|
|
47
|
+
name: TASK_TOOL,
|
|
48
|
+
description: buildTaskDescription(subagents),
|
|
49
|
+
schema: z4__default.default.object({
|
|
50
|
+
subagent: z4__default.default.enum(names).describe("The type of subagent to launch"),
|
|
51
|
+
description: z4__default.default.string().describe("A short (3-5 word) description of the task"),
|
|
52
|
+
prompt: z4__default.default.string().describe("The task for the agent to perform")
|
|
53
|
+
})
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function createTaskHandler(subagents) {
|
|
57
|
+
const { workflowId: parentWorkflowId, taskQueue: parentTaskQueue } = workflow.workflowInfo();
|
|
58
|
+
return async (args) => {
|
|
59
|
+
const config = subagents.find((s) => s.name === args.subagent);
|
|
60
|
+
if (!config) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Unknown subagent: ${args.subagent}. Available: ${subagents.map((s) => s.name).join(", ")}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const childWorkflowId = `${parentWorkflowId}-${args.subagent}-${workflow.uuid4()}`;
|
|
66
|
+
const childResult = await workflow.executeChild(config.workflowType, {
|
|
67
|
+
workflowId: childWorkflowId,
|
|
68
|
+
args: [{ prompt: args.prompt }],
|
|
69
|
+
taskQueue: config.taskQueue ?? parentTaskQueue
|
|
70
|
+
});
|
|
71
|
+
const validated = config.resultSchema ? config.resultSchema.parse(childResult) : childResult;
|
|
72
|
+
const content = typeof validated === "string" ? validated : JSON.stringify(validated, null, 2);
|
|
73
|
+
return {
|
|
74
|
+
content,
|
|
75
|
+
result: {
|
|
76
|
+
result: validated,
|
|
77
|
+
childWorkflowId
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
var createBashToolDescription = ({
|
|
83
|
+
fileTree
|
|
84
|
+
}) => `tool to execute bash commands, the file tree is: ${fileTree}`;
|
|
85
|
+
var bashTool = {
|
|
86
|
+
name: "Bash",
|
|
87
|
+
description: "tool to execute bash commands",
|
|
88
|
+
schema: z4__default.default.object({
|
|
89
|
+
command: z4__default.default.string().describe("stringified command to be executed inside the Bash")
|
|
90
|
+
}),
|
|
91
|
+
strict: true
|
|
92
|
+
};
|
|
93
|
+
var taskCreateTool = {
|
|
94
|
+
name: "TaskCreate",
|
|
95
|
+
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.
|
|
96
|
+
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
97
|
+
|
|
98
|
+
## When to Use This Tool
|
|
99
|
+
|
|
100
|
+
Use this tool proactively in these scenarios:
|
|
101
|
+
|
|
102
|
+
- Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
|
|
103
|
+
- Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
|
|
104
|
+
- User explicitly requests todo list - When the user directly asks you to use the todo list
|
|
105
|
+
- User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
|
|
106
|
+
- After receiving new instructions - Immediately capture user requirements as tasks
|
|
107
|
+
- When you start working on a task - Mark it as in_progress BEFORE beginning work
|
|
108
|
+
- After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation
|
|
109
|
+
|
|
110
|
+
## When NOT to Use This Tool
|
|
111
|
+
|
|
112
|
+
Skip using this tool when:
|
|
113
|
+
- There is only a single, straightforward task
|
|
114
|
+
- The task is trivial and tracking it provides no organizational benefit
|
|
115
|
+
- The task can be completed in less than 3 trivial steps
|
|
116
|
+
- The task is purely conversational or informational
|
|
117
|
+
|
|
118
|
+
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.
|
|
119
|
+
|
|
120
|
+
## Task Fields
|
|
121
|
+
|
|
122
|
+
- **subject**: A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")
|
|
123
|
+
- **description**: Detailed description of what needs to be done, including context and acceptance criteria
|
|
124
|
+
- **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.
|
|
125
|
+
|
|
126
|
+
**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\`.
|
|
127
|
+
|
|
128
|
+
## Tips
|
|
129
|
+
|
|
130
|
+
- Create tasks with clear, specific subjects that describe the outcome
|
|
131
|
+
- Include enough detail in the description for another agent to understand and complete the task
|
|
132
|
+
- After creating tasks, use TaskUpdate to set up dependencies (blocks/blockedBy) if needed
|
|
133
|
+
- Check TaskList first to avoid creating duplicate tasks`,
|
|
134
|
+
schema: z4__default.default.object({
|
|
135
|
+
subject: z4__default.default.string().describe(
|
|
136
|
+
'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
|
|
137
|
+
),
|
|
138
|
+
description: z4__default.default.string().describe(
|
|
139
|
+
"Detailed description of what needs to be done, including context and acceptance criteria"
|
|
140
|
+
),
|
|
141
|
+
activeForm: z4__default.default.string().describe(
|
|
142
|
+
'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.'
|
|
143
|
+
),
|
|
144
|
+
metadata: z4__default.default.record(z4__default.default.string(), z4__default.default.string()).describe("Arbitrary key-value pairs for tracking")
|
|
145
|
+
})
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/lib/tool-router.ts
|
|
149
|
+
var buildIntoolDefinitions = {
|
|
150
|
+
[bashTool.name]: bashTool,
|
|
151
|
+
[taskCreateTool.name]: taskCreateTool
|
|
152
|
+
};
|
|
153
|
+
function createToolRouter(options) {
|
|
154
|
+
const { appendToolResult } = workflow.proxyActivities({
|
|
155
|
+
startToCloseTimeout: "2m",
|
|
156
|
+
retry: {
|
|
157
|
+
maximumAttempts: 3,
|
|
158
|
+
initialInterval: "5s",
|
|
159
|
+
maximumInterval: "15m",
|
|
160
|
+
backoffCoefficient: 4
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
const toolMap = /* @__PURE__ */ new Map();
|
|
164
|
+
for (const [_key, tool] of Object.entries(options.tools)) {
|
|
165
|
+
toolMap.set(tool.name, tool);
|
|
166
|
+
}
|
|
167
|
+
if (options.subagents) {
|
|
168
|
+
toolMap.set("Task", {
|
|
169
|
+
...createTaskTool(options.subagents),
|
|
170
|
+
handler: createTaskHandler(options.subagents)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
if (options.buildInTools) {
|
|
174
|
+
for (const [key, value] of Object.entries(options.buildInTools)) {
|
|
175
|
+
if (key === bashTool.name) {
|
|
176
|
+
toolMap.set(key, {
|
|
177
|
+
...buildIntoolDefinitions[key],
|
|
178
|
+
description: createBashToolDescription({
|
|
179
|
+
fileTree: options.fileTree
|
|
180
|
+
}),
|
|
181
|
+
handler: value
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
toolMap.set(key, {
|
|
185
|
+
...buildIntoolDefinitions[key],
|
|
186
|
+
handler: value
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function processToolCall(toolCall, turn, handlerContext) {
|
|
192
|
+
const startTime = Date.now();
|
|
193
|
+
let effectiveArgs = toolCall.args;
|
|
194
|
+
if (options.hooks?.onPreToolUse) {
|
|
195
|
+
const preResult = await options.hooks.onPreToolUse({
|
|
196
|
+
toolCall,
|
|
197
|
+
threadId: options.threadId,
|
|
198
|
+
turn
|
|
199
|
+
});
|
|
200
|
+
if (preResult?.skip) {
|
|
201
|
+
await appendToolResult({
|
|
202
|
+
threadId: options.threadId,
|
|
203
|
+
toolCallId: toolCall.id,
|
|
204
|
+
content: JSON.stringify({
|
|
205
|
+
skipped: true,
|
|
206
|
+
reason: "Skipped by PreToolUse hook"
|
|
207
|
+
})
|
|
208
|
+
});
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
if (preResult?.modifiedArgs !== void 0) {
|
|
212
|
+
effectiveArgs = preResult.modifiedArgs;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const tool = toolMap.get(toolCall.name);
|
|
216
|
+
let result;
|
|
217
|
+
let content;
|
|
218
|
+
try {
|
|
219
|
+
if (tool) {
|
|
220
|
+
const response = await tool.handler(
|
|
221
|
+
effectiveArgs,
|
|
222
|
+
handlerContext ?? {}
|
|
223
|
+
);
|
|
224
|
+
result = response.result;
|
|
225
|
+
content = response.content;
|
|
226
|
+
} else {
|
|
227
|
+
result = { error: `Unknown tool: ${toolCall.name}` };
|
|
228
|
+
content = JSON.stringify(result, null, 2);
|
|
229
|
+
}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (options.hooks?.onPostToolUseFailure) {
|
|
232
|
+
const failureResult = await options.hooks.onPostToolUseFailure({
|
|
233
|
+
toolCall,
|
|
234
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
235
|
+
threadId: options.threadId,
|
|
236
|
+
turn
|
|
237
|
+
});
|
|
238
|
+
if (failureResult?.fallbackContent !== void 0) {
|
|
239
|
+
content = failureResult.fallbackContent;
|
|
240
|
+
result = { error: String(error), recovered: true };
|
|
241
|
+
} else if (failureResult?.suppress) {
|
|
242
|
+
content = JSON.stringify({ error: String(error), suppressed: true });
|
|
243
|
+
result = { error: String(error), suppressed: true };
|
|
244
|
+
} else {
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
await appendToolResult({
|
|
252
|
+
threadId: options.threadId,
|
|
253
|
+
toolCallId: toolCall.id,
|
|
254
|
+
content
|
|
255
|
+
});
|
|
256
|
+
const toolResult = {
|
|
257
|
+
toolCallId: toolCall.id,
|
|
258
|
+
name: toolCall.name,
|
|
259
|
+
result
|
|
260
|
+
};
|
|
261
|
+
if (options.hooks?.onPostToolUse) {
|
|
262
|
+
const durationMs = Date.now() - startTime;
|
|
263
|
+
await options.hooks.onPostToolUse({
|
|
264
|
+
toolCall,
|
|
265
|
+
result: toolResult,
|
|
266
|
+
threadId: options.threadId,
|
|
267
|
+
turn,
|
|
268
|
+
durationMs
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return toolResult;
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
// --- Methods from registry ---
|
|
275
|
+
hasTools() {
|
|
276
|
+
return toolMap.size > 0;
|
|
277
|
+
},
|
|
278
|
+
parseToolCall(toolCall) {
|
|
279
|
+
const tool = toolMap.get(toolCall.name);
|
|
280
|
+
if (!tool) {
|
|
281
|
+
throw new Error(`Tool ${toolCall.name} not found`);
|
|
282
|
+
}
|
|
283
|
+
const parsedArgs = tool.schema.parse(toolCall.args);
|
|
284
|
+
return {
|
|
285
|
+
id: toolCall.id ?? "",
|
|
286
|
+
name: toolCall.name,
|
|
287
|
+
args: parsedArgs
|
|
288
|
+
};
|
|
289
|
+
},
|
|
290
|
+
hasTool(name) {
|
|
291
|
+
return toolMap.has(name);
|
|
292
|
+
},
|
|
293
|
+
getToolNames() {
|
|
294
|
+
return Array.from(toolMap.keys());
|
|
295
|
+
},
|
|
296
|
+
getToolDefinitions() {
|
|
297
|
+
return Array.from(toolMap).map(([name, tool]) => ({
|
|
298
|
+
name,
|
|
299
|
+
description: tool.description,
|
|
300
|
+
schema: tool.schema,
|
|
301
|
+
strict: tool.strict,
|
|
302
|
+
max_uses: tool.max_uses
|
|
303
|
+
}));
|
|
304
|
+
},
|
|
305
|
+
// --- Methods for processing tool calls ---
|
|
306
|
+
async processToolCalls(toolCalls, context) {
|
|
307
|
+
if (toolCalls.length === 0) {
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
const turn = context?.turn ?? 0;
|
|
311
|
+
const handlerContext = context?.handlerContext;
|
|
312
|
+
if (options.parallel) {
|
|
313
|
+
const results2 = await Promise.all(
|
|
314
|
+
toolCalls.map((tc) => processToolCall(tc, turn, handlerContext))
|
|
315
|
+
);
|
|
316
|
+
return results2.filter(
|
|
317
|
+
(r) => r !== null
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
const results = [];
|
|
321
|
+
for (const toolCall of toolCalls) {
|
|
322
|
+
const result = await processToolCall(toolCall, turn, handlerContext);
|
|
323
|
+
if (result !== null) {
|
|
324
|
+
results.push(result);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return results;
|
|
328
|
+
},
|
|
329
|
+
async processToolCallsByName(toolCalls, toolName, handler, context) {
|
|
330
|
+
const matchingCalls = toolCalls.filter((tc) => tc.name === toolName);
|
|
331
|
+
if (matchingCalls.length === 0) {
|
|
332
|
+
return [];
|
|
333
|
+
}
|
|
334
|
+
const handlerContext = context?.handlerContext ?? {};
|
|
335
|
+
const processOne = async (toolCall) => {
|
|
336
|
+
const response = await handler(
|
|
337
|
+
toolCall.args,
|
|
338
|
+
handlerContext
|
|
339
|
+
);
|
|
340
|
+
await appendToolResult({
|
|
341
|
+
threadId: options.threadId,
|
|
342
|
+
toolCallId: toolCall.id,
|
|
343
|
+
content: response.content
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
toolCallId: toolCall.id,
|
|
347
|
+
name: toolCall.name,
|
|
348
|
+
result: response.result
|
|
349
|
+
};
|
|
350
|
+
};
|
|
351
|
+
if (options.parallel) {
|
|
352
|
+
return Promise.all(matchingCalls.map(processOne));
|
|
353
|
+
}
|
|
354
|
+
const results = [];
|
|
355
|
+
for (const toolCall of matchingCalls) {
|
|
356
|
+
results.push(await processOne(toolCall));
|
|
357
|
+
}
|
|
358
|
+
return results;
|
|
359
|
+
},
|
|
360
|
+
// --- Utility methods ---
|
|
361
|
+
filterByName(toolCalls, name) {
|
|
362
|
+
return toolCalls.filter(
|
|
363
|
+
(tc) => tc.name === name
|
|
364
|
+
);
|
|
365
|
+
},
|
|
366
|
+
hasToolCall(toolCalls, name) {
|
|
367
|
+
return toolCalls.some((tc) => tc.name === name);
|
|
368
|
+
},
|
|
369
|
+
getResultsByName(results, name) {
|
|
370
|
+
return results.filter((r) => r.name === name);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function hasNoOtherToolCalls(toolCalls, excludeName) {
|
|
375
|
+
return toolCalls.filter((tc) => tc.name !== excludeName).length === 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/lib/session.ts
|
|
379
|
+
async function resolvePrompt(prompt) {
|
|
380
|
+
if (typeof prompt === "function") {
|
|
381
|
+
return prompt();
|
|
382
|
+
}
|
|
383
|
+
return prompt;
|
|
384
|
+
}
|
|
385
|
+
var createSession = async ({
|
|
386
|
+
threadId,
|
|
387
|
+
agentName,
|
|
388
|
+
maxTurns = 50,
|
|
389
|
+
metadata = {},
|
|
390
|
+
runAgent,
|
|
391
|
+
baseSystemPrompt,
|
|
392
|
+
instructionsPrompt,
|
|
393
|
+
buildContextMessage,
|
|
394
|
+
buildFileTree = async () => "",
|
|
395
|
+
subagents,
|
|
396
|
+
tools = {},
|
|
397
|
+
processToolsInParallel = true,
|
|
398
|
+
buildInTools = {},
|
|
399
|
+
hooks = {}
|
|
400
|
+
}) => {
|
|
401
|
+
const {
|
|
402
|
+
initializeThread,
|
|
403
|
+
appendHumanMessage,
|
|
404
|
+
parseToolCalls,
|
|
405
|
+
appendToolResult,
|
|
406
|
+
appendSystemMessage
|
|
407
|
+
} = workflow.proxyActivities({
|
|
408
|
+
startToCloseTimeout: "30m",
|
|
409
|
+
retry: {
|
|
410
|
+
maximumAttempts: 6,
|
|
411
|
+
initialInterval: "5s",
|
|
412
|
+
maximumInterval: "15m",
|
|
413
|
+
backoffCoefficient: 4
|
|
414
|
+
},
|
|
415
|
+
heartbeatTimeout: "5m"
|
|
416
|
+
});
|
|
417
|
+
const fileTree = await buildFileTree();
|
|
418
|
+
const toolRouter = createToolRouter({
|
|
419
|
+
tools,
|
|
420
|
+
threadId,
|
|
421
|
+
hooks,
|
|
422
|
+
buildInTools,
|
|
423
|
+
fileTree,
|
|
424
|
+
subagents,
|
|
425
|
+
parallel: processToolsInParallel
|
|
426
|
+
});
|
|
427
|
+
const callSessionEnd = async (exitReason, turns) => {
|
|
428
|
+
if (hooks.onSessionEnd) {
|
|
429
|
+
await hooks.onSessionEnd({
|
|
430
|
+
threadId,
|
|
431
|
+
agentName,
|
|
432
|
+
exitReason,
|
|
433
|
+
turns,
|
|
434
|
+
metadata
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
return {
|
|
439
|
+
runSession: async ({ stateManager }) => {
|
|
440
|
+
if (hooks.onSessionStart) {
|
|
441
|
+
await hooks.onSessionStart({
|
|
442
|
+
threadId,
|
|
443
|
+
agentName,
|
|
444
|
+
metadata
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
stateManager.setTools(toolRouter.getToolDefinitions());
|
|
448
|
+
await initializeThread(threadId);
|
|
449
|
+
await appendSystemMessage(
|
|
450
|
+
threadId,
|
|
451
|
+
[
|
|
452
|
+
await resolvePrompt(baseSystemPrompt),
|
|
453
|
+
await resolvePrompt(instructionsPrompt)
|
|
454
|
+
].join("\n")
|
|
455
|
+
);
|
|
456
|
+
await appendHumanMessage(threadId, await buildContextMessage());
|
|
457
|
+
let exitReason = "completed";
|
|
458
|
+
try {
|
|
459
|
+
while (stateManager.isRunning() && !stateManager.isTerminal() && stateManager.getTurns() < maxTurns) {
|
|
460
|
+
stateManager.incrementTurns();
|
|
461
|
+
const currentTurn = stateManager.getTurns();
|
|
462
|
+
const { message, stopReason } = await runAgent({
|
|
463
|
+
threadId,
|
|
464
|
+
agentName,
|
|
465
|
+
metadata
|
|
466
|
+
});
|
|
467
|
+
if (stopReason === "end_turn") {
|
|
468
|
+
stateManager.complete();
|
|
469
|
+
exitReason = "completed";
|
|
470
|
+
return message;
|
|
471
|
+
}
|
|
472
|
+
if (!toolRouter.hasTools()) {
|
|
473
|
+
stateManager.complete();
|
|
474
|
+
exitReason = "completed";
|
|
475
|
+
return message;
|
|
476
|
+
}
|
|
477
|
+
const rawToolCalls = await parseToolCalls(message);
|
|
478
|
+
const parsedToolCalls = rawToolCalls.filter((tc) => tc.name !== "Task").map((tc) => toolRouter.parseToolCall(tc));
|
|
479
|
+
const taskToolCalls = subagents && subagents.length > 0 ? rawToolCalls.filter((tc) => tc.name === "Task").map((tc) => {
|
|
480
|
+
const parsedArgs = createTaskTool(subagents).schema.parse(
|
|
481
|
+
tc.args
|
|
482
|
+
);
|
|
483
|
+
return {
|
|
484
|
+
id: tc.id ?? "",
|
|
485
|
+
name: tc.name,
|
|
486
|
+
args: parsedArgs
|
|
487
|
+
};
|
|
488
|
+
}) : [];
|
|
489
|
+
await toolRouter.processToolCalls(
|
|
490
|
+
[...parsedToolCalls, ...taskToolCalls],
|
|
491
|
+
{
|
|
492
|
+
turn: currentTurn
|
|
493
|
+
}
|
|
494
|
+
);
|
|
495
|
+
if (stateManager.getStatus() === "WAITING_FOR_INPUT") {
|
|
496
|
+
exitReason = "waiting_for_input";
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (stateManager.getTurns() >= maxTurns && stateManager.isRunning()) {
|
|
501
|
+
exitReason = "max_turns";
|
|
502
|
+
}
|
|
503
|
+
} catch (error) {
|
|
504
|
+
exitReason = "failed";
|
|
505
|
+
throw error;
|
|
506
|
+
} finally {
|
|
507
|
+
await callSessionEnd(exitReason, stateManager.getTurns());
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// src/lib/types.ts
|
|
515
|
+
function isTerminalStatus(status) {
|
|
516
|
+
return status === "COMPLETED" || status === "FAILED" || status === "CANCELLED";
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/lib/state-manager.ts
|
|
520
|
+
var getStateQuery = workflow.defineQuery("getState");
|
|
521
|
+
function createAgentStateManager(initialState) {
|
|
522
|
+
let status = initialState?.status ?? "RUNNING";
|
|
523
|
+
let version = initialState?.version ?? 0;
|
|
524
|
+
let turns = initialState?.turns ?? 0;
|
|
525
|
+
let tools = initialState?.tools ?? [];
|
|
526
|
+
const tasks = new Map(initialState?.tasks);
|
|
527
|
+
const {
|
|
528
|
+
status: _,
|
|
529
|
+
version: __,
|
|
530
|
+
turns: ___,
|
|
531
|
+
tasks: ____,
|
|
532
|
+
tools: _____,
|
|
533
|
+
...custom
|
|
534
|
+
} = initialState ?? {};
|
|
535
|
+
const customState = custom;
|
|
536
|
+
function buildState() {
|
|
537
|
+
return {
|
|
538
|
+
status,
|
|
539
|
+
version,
|
|
540
|
+
turns,
|
|
541
|
+
tools,
|
|
542
|
+
...customState
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
workflow.setHandler(getStateQuery, () => {
|
|
546
|
+
return buildState();
|
|
547
|
+
});
|
|
548
|
+
return {
|
|
549
|
+
getStatus() {
|
|
550
|
+
return status;
|
|
551
|
+
},
|
|
552
|
+
isRunning() {
|
|
553
|
+
return status === "RUNNING";
|
|
554
|
+
},
|
|
555
|
+
isTerminal() {
|
|
556
|
+
return isTerminalStatus(status);
|
|
557
|
+
},
|
|
558
|
+
getTurns() {
|
|
559
|
+
return turns;
|
|
560
|
+
},
|
|
561
|
+
getVersion() {
|
|
562
|
+
return version;
|
|
563
|
+
},
|
|
564
|
+
run() {
|
|
565
|
+
status = "RUNNING";
|
|
566
|
+
version++;
|
|
567
|
+
},
|
|
568
|
+
waitForInput() {
|
|
569
|
+
status = "WAITING_FOR_INPUT";
|
|
570
|
+
version++;
|
|
571
|
+
},
|
|
572
|
+
complete() {
|
|
573
|
+
status = "COMPLETED";
|
|
574
|
+
version++;
|
|
575
|
+
},
|
|
576
|
+
fail() {
|
|
577
|
+
status = "FAILED";
|
|
578
|
+
version++;
|
|
579
|
+
},
|
|
580
|
+
cancel() {
|
|
581
|
+
status = "CANCELLED";
|
|
582
|
+
version++;
|
|
583
|
+
},
|
|
584
|
+
incrementVersion() {
|
|
585
|
+
version++;
|
|
586
|
+
},
|
|
587
|
+
incrementTurns() {
|
|
588
|
+
turns++;
|
|
589
|
+
},
|
|
590
|
+
get(key) {
|
|
591
|
+
return customState[key];
|
|
592
|
+
},
|
|
593
|
+
set(key, value) {
|
|
594
|
+
customState[key] = value;
|
|
595
|
+
version++;
|
|
596
|
+
},
|
|
597
|
+
getCurrentState() {
|
|
598
|
+
return buildState();
|
|
599
|
+
},
|
|
600
|
+
shouldReturnFromWait(lastKnownVersion) {
|
|
601
|
+
return version > lastKnownVersion || isTerminalStatus(status);
|
|
602
|
+
},
|
|
603
|
+
getTasks() {
|
|
604
|
+
return Array.from(tasks.values());
|
|
605
|
+
},
|
|
606
|
+
getTask(id) {
|
|
607
|
+
return tasks.get(id);
|
|
608
|
+
},
|
|
609
|
+
setTask(task) {
|
|
610
|
+
tasks.set(task.id, task);
|
|
611
|
+
version++;
|
|
612
|
+
},
|
|
613
|
+
setTools(newTools) {
|
|
614
|
+
tools = newTools;
|
|
615
|
+
},
|
|
616
|
+
deleteTask(id) {
|
|
617
|
+
const deleted = tasks.delete(id);
|
|
618
|
+
if (deleted) {
|
|
619
|
+
version++;
|
|
620
|
+
}
|
|
621
|
+
return deleted;
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
var AGENT_HANDLER_NAMES = {
|
|
626
|
+
getAgentState: "getAgentState",
|
|
627
|
+
waitForStateChange: "waitForStateChange",
|
|
628
|
+
addMessage: "addMessage"
|
|
629
|
+
};
|
|
630
|
+
var askUserQuestionTool = {
|
|
631
|
+
name: "AskUserQuestion",
|
|
632
|
+
description: `Use this tool when you need to ask the user questions during execution. This allows you to:
|
|
633
|
+
|
|
634
|
+
1. Gather user preferences or requirements
|
|
635
|
+
2. Clarify ambiguous instructions
|
|
636
|
+
3. Get decisions on implementation choices as you work
|
|
637
|
+
4. Offer choices to the user about what direction to take.
|
|
638
|
+
|
|
639
|
+
Usage notes:
|
|
640
|
+
|
|
641
|
+
* Users will always be able to select "Other" to provide custom text input
|
|
642
|
+
* Use multiSelect: true to allow multiple answers to be selected for a question
|
|
643
|
+
* If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
|
|
644
|
+
`,
|
|
645
|
+
schema: z4__default.default.object({
|
|
646
|
+
questions: z4__default.default.array(
|
|
647
|
+
z4__default.default.object({
|
|
648
|
+
question: z4__default.default.string().describe("The full question text to display"),
|
|
649
|
+
header: z4__default.default.string().describe("Short label for the question (max 12 characters)"),
|
|
650
|
+
options: z4__default.default.array(
|
|
651
|
+
z4__default.default.object({
|
|
652
|
+
label: z4__default.default.string(),
|
|
653
|
+
description: z4__default.default.string()
|
|
654
|
+
})
|
|
655
|
+
).min(0).max(4).describe("Array of 0-4 choices, each with label and description"),
|
|
656
|
+
multiSelect: z4__default.default.boolean().describe("If true, users can select multiple options")
|
|
657
|
+
})
|
|
658
|
+
)
|
|
659
|
+
}),
|
|
660
|
+
strict: true
|
|
661
|
+
};
|
|
662
|
+
var globTool = {
|
|
663
|
+
name: "Glob",
|
|
664
|
+
description: `Search for files matching a glob pattern within the available file system.
|
|
665
|
+
|
|
666
|
+
Usage:
|
|
667
|
+
- Use glob patterns like "**/*.ts" to find all TypeScript files
|
|
668
|
+
- Use "docs/**" to find all files in the docs directory
|
|
669
|
+
- Patterns are matched against virtual paths in the file system
|
|
670
|
+
|
|
671
|
+
Examples:
|
|
672
|
+
- "*.md" - Find all markdown files in the root
|
|
673
|
+
- "**/*.test.ts" - Find all test files recursively
|
|
674
|
+
- "src/**/*.ts" - Find all TypeScript files in src directory
|
|
675
|
+
`,
|
|
676
|
+
schema: z4.z.object({
|
|
677
|
+
pattern: z4.z.string().describe("Glob pattern to match files against"),
|
|
678
|
+
root: z4.z.string().optional().describe("Optional root directory to search from")
|
|
679
|
+
}),
|
|
680
|
+
strict: true
|
|
681
|
+
};
|
|
682
|
+
var grepTool = {
|
|
683
|
+
name: "Grep",
|
|
684
|
+
description: `Search file contents for a pattern within the available file system.
|
|
685
|
+
|
|
686
|
+
Usage:
|
|
687
|
+
- Searches for a regex pattern across file contents
|
|
688
|
+
- Returns matching lines with file paths and line numbers
|
|
689
|
+
- Can filter by file patterns and limit results
|
|
690
|
+
|
|
691
|
+
Examples:
|
|
692
|
+
- Search for "TODO" in all files
|
|
693
|
+
- Search for function definitions with "function.*handleClick"
|
|
694
|
+
- Search case-insensitively with ignoreCase: true
|
|
695
|
+
`,
|
|
696
|
+
schema: z4.z.object({
|
|
697
|
+
pattern: z4.z.string().describe("Regex pattern to search for in file contents"),
|
|
698
|
+
ignoreCase: z4.z.boolean().optional().describe("Case-insensitive search (default: false)"),
|
|
699
|
+
maxMatches: z4.z.number().optional().describe("Maximum number of matches to return (default: 50)"),
|
|
700
|
+
includePatterns: z4.z.array(z4.z.string()).optional().describe("Glob patterns to include (e.g., ['*.ts', '*.js'])"),
|
|
701
|
+
excludePatterns: z4.z.array(z4.z.string()).optional().describe("Glob patterns to exclude (e.g., ['*.test.ts'])"),
|
|
702
|
+
contextLines: z4.z.number().optional().describe("Number of context lines to show around matches")
|
|
703
|
+
}),
|
|
704
|
+
strict: true
|
|
705
|
+
};
|
|
706
|
+
var readTool = {
|
|
707
|
+
name: "FileRead",
|
|
708
|
+
description: `Read file contents with optional pagination.
|
|
709
|
+
|
|
710
|
+
Usage:
|
|
711
|
+
- Provide the virtual path to the file you want to read
|
|
712
|
+
- Supports text files, images, and PDFs
|
|
713
|
+
- For large files, use offset and limit to read specific portions
|
|
714
|
+
|
|
715
|
+
The tool returns the file content in an appropriate format:
|
|
716
|
+
- Text files: Plain text content
|
|
717
|
+
- Images: Base64-encoded image data
|
|
718
|
+
- PDFs: Extracted text content
|
|
719
|
+
`,
|
|
720
|
+
schema: z4.z.object({
|
|
721
|
+
path: z4.z.string().describe("Virtual path to the file to read"),
|
|
722
|
+
offset: z4.z.number().optional().describe(
|
|
723
|
+
"Line number to start reading from (1-indexed, for text files)"
|
|
724
|
+
),
|
|
725
|
+
limit: z4.z.number().optional().describe("Maximum number of lines to read (for text files)")
|
|
726
|
+
}),
|
|
727
|
+
strict: true
|
|
728
|
+
};
|
|
729
|
+
var writeTool = {
|
|
730
|
+
name: "FileWrite",
|
|
731
|
+
description: `Create or overwrite a file with new content.
|
|
732
|
+
|
|
733
|
+
Usage:
|
|
734
|
+
- Provide the absolute virtual path to the file
|
|
735
|
+
- The file will be created if it doesn't exist
|
|
736
|
+
- If the file exists, it will be completely overwritten
|
|
737
|
+
|
|
738
|
+
IMPORTANT:
|
|
739
|
+
- You must read the file first (in this session) before writing to it
|
|
740
|
+
- This is an atomic write operation - the entire file is replaced
|
|
741
|
+
- Path must be absolute (e.g., "/docs/readme.md", not "docs/readme.md")
|
|
742
|
+
`,
|
|
743
|
+
schema: z4.z.object({
|
|
744
|
+
file_path: z4.z.string().describe("The absolute virtual path to the file to write"),
|
|
745
|
+
content: z4.z.string().describe("The content to write to the file")
|
|
746
|
+
}),
|
|
747
|
+
strict: true
|
|
748
|
+
};
|
|
749
|
+
var editTool = {
|
|
750
|
+
name: "FileEdit",
|
|
751
|
+
description: `Edit specific sections of a file by replacing text.
|
|
752
|
+
|
|
753
|
+
Usage:
|
|
754
|
+
- Provide the exact text to find and replace
|
|
755
|
+
- The old_string must match exactly (whitespace-sensitive)
|
|
756
|
+
- By default, only replaces the first occurrence
|
|
757
|
+
- Use replace_all: true to replace all occurrences
|
|
758
|
+
|
|
759
|
+
IMPORTANT:
|
|
760
|
+
- You must read the file first (in this session) before editing it
|
|
761
|
+
- old_string must be unique in the file (unless using replace_all)
|
|
762
|
+
- The operation fails if old_string is not found
|
|
763
|
+
- old_string and new_string must be different
|
|
764
|
+
`,
|
|
765
|
+
schema: z4.z.object({
|
|
766
|
+
file_path: z4.z.string().describe("The absolute virtual path to the file to modify"),
|
|
767
|
+
old_string: z4.z.string().describe("The exact text to replace"),
|
|
768
|
+
new_string: z4.z.string().describe(
|
|
769
|
+
"The text to replace it with (must be different from old_string)"
|
|
770
|
+
),
|
|
771
|
+
replace_all: z4.z.boolean().optional().describe(
|
|
772
|
+
"If true, replace all occurrences of old_string (default: false)"
|
|
773
|
+
)
|
|
774
|
+
}),
|
|
775
|
+
strict: true
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
// src/tools/task-create/handler.ts
|
|
779
|
+
function createTaskCreateHandler({
|
|
780
|
+
stateManager,
|
|
781
|
+
idGenerator
|
|
782
|
+
}) {
|
|
783
|
+
return (args) => {
|
|
784
|
+
const task = {
|
|
785
|
+
id: idGenerator(),
|
|
786
|
+
subject: args.subject,
|
|
787
|
+
description: args.description,
|
|
788
|
+
activeForm: args.activeForm,
|
|
789
|
+
status: "pending",
|
|
790
|
+
metadata: args.metadata ?? {},
|
|
791
|
+
blockedBy: [],
|
|
792
|
+
blocks: []
|
|
793
|
+
};
|
|
794
|
+
stateManager.setTask(task);
|
|
795
|
+
return {
|
|
796
|
+
content: JSON.stringify(task, null, 2),
|
|
797
|
+
result: task
|
|
798
|
+
};
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
var taskGetTool = {
|
|
802
|
+
name: "TaskGet",
|
|
803
|
+
description: `Retrieve full task details including dependencies.`,
|
|
804
|
+
schema: z4__default.default.object({
|
|
805
|
+
taskId: z4__default.default.string().describe("The ID of the task to get")
|
|
806
|
+
})
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
// src/tools/task-get/handler.ts
|
|
810
|
+
function createTaskGetHandler(stateManager) {
|
|
811
|
+
return (args) => {
|
|
812
|
+
const task = stateManager.getTask(args.taskId) ?? null;
|
|
813
|
+
if (!task) {
|
|
814
|
+
return {
|
|
815
|
+
content: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
|
|
816
|
+
result: null
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
content: JSON.stringify(task, null, 2),
|
|
821
|
+
result: task
|
|
822
|
+
};
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
var taskListTool = {
|
|
826
|
+
name: "TaskList",
|
|
827
|
+
description: `List all tasks with current state.`,
|
|
828
|
+
schema: z4__default.default.object({})
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// src/tools/task-list/handler.ts
|
|
832
|
+
function createTaskListHandler(stateManager) {
|
|
833
|
+
return (_args) => {
|
|
834
|
+
const taskList = stateManager.getTasks();
|
|
835
|
+
return {
|
|
836
|
+
content: JSON.stringify(taskList, null, 2),
|
|
837
|
+
result: taskList
|
|
838
|
+
};
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
var taskUpdateTool = {
|
|
842
|
+
name: "TaskUpdate",
|
|
843
|
+
description: `Update status, add blockers, modify details.`,
|
|
844
|
+
schema: z4__default.default.object({
|
|
845
|
+
taskId: z4__default.default.string().describe("The ID of the task to get"),
|
|
846
|
+
status: z4__default.default.enum(["pending", "in_progress", "completed"]).describe("The status of the task"),
|
|
847
|
+
addBlockedBy: z4__default.default.array(z4__default.default.string()).describe("The IDs of the tasks that are blocking this task"),
|
|
848
|
+
addBlocks: z4__default.default.array(z4__default.default.string()).describe("The IDs of the tasks that this task is blocking")
|
|
849
|
+
})
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
// src/tools/task-update/handler.ts
|
|
853
|
+
function createTaskUpdateHandler(stateManager) {
|
|
854
|
+
return (args) => {
|
|
855
|
+
const task = stateManager.getTask(args.taskId);
|
|
856
|
+
if (!task) {
|
|
857
|
+
return {
|
|
858
|
+
content: JSON.stringify({ error: `Task not found: ${args.taskId}` }),
|
|
859
|
+
result: null
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
if (args.status) {
|
|
863
|
+
task.status = args.status;
|
|
864
|
+
}
|
|
865
|
+
if (args.addBlockedBy) {
|
|
866
|
+
for (const blockerId of args.addBlockedBy) {
|
|
867
|
+
if (!task.blockedBy.includes(blockerId)) {
|
|
868
|
+
task.blockedBy.push(blockerId);
|
|
869
|
+
}
|
|
870
|
+
const blockerTask = stateManager.getTask(blockerId);
|
|
871
|
+
if (blockerTask && !blockerTask.blocks.includes(task.id)) {
|
|
872
|
+
blockerTask.blocks.push(task.id);
|
|
873
|
+
stateManager.setTask(blockerTask);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
if (args.addBlocks) {
|
|
878
|
+
for (const blockedId of args.addBlocks) {
|
|
879
|
+
if (!task.blocks.includes(blockedId)) {
|
|
880
|
+
task.blocks.push(blockedId);
|
|
881
|
+
}
|
|
882
|
+
const blockedTask = stateManager.getTask(blockedId);
|
|
883
|
+
if (blockedTask && !blockedTask.blockedBy.includes(task.id)) {
|
|
884
|
+
blockedTask.blockedBy.push(task.id);
|
|
885
|
+
stateManager.setTask(blockedTask);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
stateManager.setTask(task);
|
|
890
|
+
return {
|
|
891
|
+
content: JSON.stringify(task, null, 2),
|
|
892
|
+
result: task
|
|
893
|
+
};
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// node_modules/uuid/dist/esm-node/stringify.js
|
|
898
|
+
var byteToHex = [];
|
|
899
|
+
for (let i = 0; i < 256; ++i) {
|
|
900
|
+
byteToHex.push((i + 256).toString(16).slice(1));
|
|
901
|
+
}
|
|
902
|
+
function unsafeStringify(arr, offset = 0) {
|
|
903
|
+
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
|
904
|
+
}
|
|
905
|
+
var rnds8Pool = new Uint8Array(256);
|
|
906
|
+
var poolPtr = rnds8Pool.length;
|
|
907
|
+
function rng() {
|
|
908
|
+
if (poolPtr > rnds8Pool.length - 16) {
|
|
909
|
+
crypto__default.default.randomFillSync(rnds8Pool);
|
|
910
|
+
poolPtr = 0;
|
|
911
|
+
}
|
|
912
|
+
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
913
|
+
}
|
|
914
|
+
var native_default = {
|
|
915
|
+
randomUUID: crypto__default.default.randomUUID
|
|
916
|
+
};
|
|
917
|
+
|
|
918
|
+
// node_modules/uuid/dist/esm-node/v4.js
|
|
919
|
+
function v4(options, buf, offset) {
|
|
920
|
+
if (native_default.randomUUID && !buf && !options) {
|
|
921
|
+
return native_default.randomUUID();
|
|
922
|
+
}
|
|
923
|
+
options = options || {};
|
|
924
|
+
const rnds = options.random || (options.rng || rng)();
|
|
925
|
+
rnds[6] = rnds[6] & 15 | 64;
|
|
926
|
+
rnds[8] = rnds[8] & 63 | 128;
|
|
927
|
+
if (buf) {
|
|
928
|
+
offset = offset || 0;
|
|
929
|
+
for (let i = 0; i < 16; ++i) {
|
|
930
|
+
buf[offset + i] = rnds[i];
|
|
931
|
+
}
|
|
932
|
+
return buf;
|
|
933
|
+
}
|
|
934
|
+
return unsafeStringify(rnds);
|
|
935
|
+
}
|
|
936
|
+
var v4_default = v4;
|
|
937
|
+
|
|
938
|
+
// src/lib/thread-manager.ts
|
|
939
|
+
var THREAD_TTL_SECONDS = 60 * 60 * 24 * 90;
|
|
940
|
+
function getThreadKey(threadId, key) {
|
|
941
|
+
return `thread:${threadId}:${key}`;
|
|
942
|
+
}
|
|
943
|
+
function createThreadManager(config) {
|
|
944
|
+
const { redis, threadId, key = "messages" } = config;
|
|
945
|
+
const redisKey = getThreadKey(threadId, key);
|
|
946
|
+
return {
|
|
947
|
+
async initialize() {
|
|
948
|
+
await redis.del(redisKey);
|
|
949
|
+
},
|
|
950
|
+
async load() {
|
|
951
|
+
const data = await redis.lrange(redisKey, 0, -1);
|
|
952
|
+
return data.map((item) => JSON.parse(item));
|
|
953
|
+
},
|
|
954
|
+
async append(messages) {
|
|
955
|
+
if (messages.length > 0) {
|
|
956
|
+
await redis.rpush(redisKey, ...messages.map((m) => JSON.stringify(m)));
|
|
957
|
+
await redis.expire(redisKey, THREAD_TTL_SECONDS);
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
async delete() {
|
|
961
|
+
await redis.del(redisKey);
|
|
962
|
+
},
|
|
963
|
+
createHumanMessage(content) {
|
|
964
|
+
return new messages.HumanMessage({
|
|
965
|
+
id: v4_default(),
|
|
966
|
+
content
|
|
967
|
+
}).toDict();
|
|
968
|
+
},
|
|
969
|
+
createAIMessage(content, kwargs) {
|
|
970
|
+
return new messages.AIMessage({
|
|
971
|
+
id: v4_default(),
|
|
972
|
+
content,
|
|
973
|
+
additional_kwargs: kwargs ? {
|
|
974
|
+
header: kwargs.header,
|
|
975
|
+
options: kwargs.options,
|
|
976
|
+
multiSelect: kwargs.multiSelect
|
|
977
|
+
} : void 0
|
|
978
|
+
}).toDict();
|
|
979
|
+
},
|
|
980
|
+
createToolMessage(content, toolCallId) {
|
|
981
|
+
return new messages.ToolMessage({
|
|
982
|
+
// Cast needed due to langchain type compatibility
|
|
983
|
+
content,
|
|
984
|
+
tool_call_id: toolCallId
|
|
985
|
+
}).toDict();
|
|
986
|
+
},
|
|
987
|
+
createSystemMessage(content) {
|
|
988
|
+
return new messages.SystemMessage({
|
|
989
|
+
content
|
|
990
|
+
}).toDict();
|
|
991
|
+
},
|
|
992
|
+
async appendSystemMessage(content) {
|
|
993
|
+
const message = this.createSystemMessage(content);
|
|
994
|
+
await this.append([message]);
|
|
995
|
+
},
|
|
996
|
+
async appendHumanMessage(content) {
|
|
997
|
+
const message = this.createHumanMessage(content);
|
|
998
|
+
await this.append([message]);
|
|
999
|
+
},
|
|
1000
|
+
async appendToolMessage(content, toolCallId) {
|
|
1001
|
+
const message = this.createToolMessage(content, toolCallId);
|
|
1002
|
+
await this.append([message]);
|
|
1003
|
+
},
|
|
1004
|
+
async appendAIMessage(content) {
|
|
1005
|
+
const message = this.createAIMessage(content);
|
|
1006
|
+
await this.append([message]);
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
function createSharedActivities(redis) {
|
|
1011
|
+
return {
|
|
1012
|
+
async appendSystemMessage(threadId, content) {
|
|
1013
|
+
const thread = createThreadManager({ redis, threadId });
|
|
1014
|
+
await thread.appendSystemMessage(content);
|
|
1015
|
+
},
|
|
1016
|
+
async appendToolResult(config) {
|
|
1017
|
+
const { threadId, toolCallId, content } = config;
|
|
1018
|
+
const thread = createThreadManager({ redis, threadId });
|
|
1019
|
+
await thread.appendToolMessage(content, toolCallId);
|
|
1020
|
+
},
|
|
1021
|
+
async initializeThread(threadId) {
|
|
1022
|
+
const thread = createThreadManager({ redis, threadId });
|
|
1023
|
+
await thread.initialize();
|
|
1024
|
+
},
|
|
1025
|
+
async appendThreadMessages(threadId, messages) {
|
|
1026
|
+
const thread = createThreadManager({ redis, threadId });
|
|
1027
|
+
await thread.append(messages);
|
|
1028
|
+
},
|
|
1029
|
+
async appendHumanMessage(threadId, content) {
|
|
1030
|
+
const thread = createThreadManager({ redis, threadId });
|
|
1031
|
+
await thread.appendHumanMessage(content);
|
|
1032
|
+
},
|
|
1033
|
+
async parseToolCalls(storedMessage) {
|
|
1034
|
+
const message = messages.mapStoredMessageToChatMessage(storedMessage);
|
|
1035
|
+
const toolCalls = message.tool_calls ?? [];
|
|
1036
|
+
return toolCalls.map((toolCall) => ({
|
|
1037
|
+
id: toolCall.id,
|
|
1038
|
+
name: toolCall.name,
|
|
1039
|
+
args: toolCall.args
|
|
1040
|
+
}));
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// src/plugin.ts
|
|
1046
|
+
var ZeitlichPlugin = class extends plugin.SimplePlugin {
|
|
1047
|
+
constructor(options) {
|
|
1048
|
+
super({
|
|
1049
|
+
name: "ZeitlichPlugin",
|
|
1050
|
+
activities: createSharedActivities(options.redis)
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
async function invokeModel({
|
|
1055
|
+
redis,
|
|
1056
|
+
model,
|
|
1057
|
+
client,
|
|
1058
|
+
config: { threadId, agentName }
|
|
1059
|
+
}) {
|
|
1060
|
+
const thread = createThreadManager({ redis, threadId });
|
|
1061
|
+
const runId = v4_default();
|
|
1062
|
+
const info = activity.Context.current().info;
|
|
1063
|
+
const parentWorkflowId = info.workflowExecution.workflowId;
|
|
1064
|
+
const parentRunId = info.workflowExecution.runId;
|
|
1065
|
+
const handle = client.getHandle(parentWorkflowId, parentRunId);
|
|
1066
|
+
const { tools } = await handle.query(getStateQuery);
|
|
1067
|
+
const messages$1 = await thread.load();
|
|
1068
|
+
const response = await model.invoke(
|
|
1069
|
+
[...messages.mapStoredMessagesToChatMessages(messages$1)],
|
|
1070
|
+
{
|
|
1071
|
+
runName: agentName,
|
|
1072
|
+
runId,
|
|
1073
|
+
metadata: { thread_id: threadId },
|
|
1074
|
+
tools
|
|
1075
|
+
}
|
|
1076
|
+
);
|
|
1077
|
+
await thread.append([response.toDict()]);
|
|
1078
|
+
return {
|
|
1079
|
+
message: response.toDict(),
|
|
1080
|
+
stopReason: response.response_metadata?.stop_reason ?? null,
|
|
1081
|
+
usage: {
|
|
1082
|
+
input_tokens: response.usage_metadata?.input_tokens,
|
|
1083
|
+
output_tokens: response.usage_metadata?.output_tokens,
|
|
1084
|
+
total_tokens: response.usage_metadata?.total_tokens
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
var handleAskUserQuestionToolResult = async (args) => {
|
|
1089
|
+
const messages$1 = args.questions.map(
|
|
1090
|
+
({ question, header, options, multiSelect }) => new messages.AIMessage({
|
|
1091
|
+
content: question,
|
|
1092
|
+
additional_kwargs: {
|
|
1093
|
+
header,
|
|
1094
|
+
options,
|
|
1095
|
+
multiSelect
|
|
1096
|
+
}
|
|
1097
|
+
}).toDict()
|
|
1098
|
+
);
|
|
1099
|
+
return { content: "Question submitted", result: { chatMessages: messages$1 } };
|
|
1100
|
+
};
|
|
1101
|
+
async function globHandler(_args, fs) {
|
|
1102
|
+
new justBash.Bash({ fs });
|
|
1103
|
+
return Promise.resolve({
|
|
1104
|
+
content: "Hello, world!",
|
|
1105
|
+
result: { files: [] }
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// src/tools/edit/handler.ts
|
|
1110
|
+
function escapeRegExp(str) {
|
|
1111
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1112
|
+
}
|
|
1113
|
+
async function editHandler(args, fs) {
|
|
1114
|
+
const { file_path, old_string, new_string, replace_all = false } = args;
|
|
1115
|
+
if (old_string === new_string) {
|
|
1116
|
+
return {
|
|
1117
|
+
content: `Error: old_string and new_string must be different.`,
|
|
1118
|
+
result: {
|
|
1119
|
+
path: file_path,
|
|
1120
|
+
success: false,
|
|
1121
|
+
replacements: 0
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
try {
|
|
1126
|
+
const exists = await fs.exists(file_path);
|
|
1127
|
+
if (!exists) {
|
|
1128
|
+
return {
|
|
1129
|
+
content: `Error: File "${file_path}" does not exist.`,
|
|
1130
|
+
result: {
|
|
1131
|
+
path: file_path,
|
|
1132
|
+
success: false,
|
|
1133
|
+
replacements: 0
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
const content = await fs.readFile(file_path);
|
|
1138
|
+
if (!content.includes(old_string)) {
|
|
1139
|
+
return {
|
|
1140
|
+
content: `Error: Could not find the specified text in "${file_path}". Make sure old_string matches exactly (whitespace-sensitive).`,
|
|
1141
|
+
result: {
|
|
1142
|
+
path: file_path,
|
|
1143
|
+
success: false,
|
|
1144
|
+
replacements: 0
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
const escapedOldString = escapeRegExp(old_string);
|
|
1149
|
+
const globalRegex = new RegExp(escapedOldString, "g");
|
|
1150
|
+
const occurrences = (content.match(globalRegex) || []).length;
|
|
1151
|
+
if (!replace_all && occurrences > 1) {
|
|
1152
|
+
return {
|
|
1153
|
+
content: `Error: old_string appears ${occurrences} times in "${file_path}". Either provide more context to make it unique, or use replace_all: true.`,
|
|
1154
|
+
result: {
|
|
1155
|
+
path: file_path,
|
|
1156
|
+
success: false,
|
|
1157
|
+
replacements: 0
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
let newContent;
|
|
1162
|
+
let replacements;
|
|
1163
|
+
if (replace_all) {
|
|
1164
|
+
newContent = content.split(old_string).join(new_string);
|
|
1165
|
+
replacements = occurrences;
|
|
1166
|
+
} else {
|
|
1167
|
+
const index = content.indexOf(old_string);
|
|
1168
|
+
newContent = content.slice(0, index) + new_string + content.slice(index + old_string.length);
|
|
1169
|
+
replacements = 1;
|
|
1170
|
+
}
|
|
1171
|
+
await fs.writeFile(file_path, newContent);
|
|
1172
|
+
const summary = replace_all ? `Replaced ${replacements} occurrence(s)` : `Replaced 1 occurrence`;
|
|
1173
|
+
return {
|
|
1174
|
+
content: `${summary} in ${file_path}`,
|
|
1175
|
+
result: {
|
|
1176
|
+
path: file_path,
|
|
1177
|
+
success: true,
|
|
1178
|
+
replacements
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1183
|
+
return {
|
|
1184
|
+
content: `Error editing file "${file_path}": ${message}`,
|
|
1185
|
+
result: {
|
|
1186
|
+
path: file_path,
|
|
1187
|
+
success: false,
|
|
1188
|
+
replacements: 0
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
var handleBashTool = (fs) => async (args, _context) => {
|
|
1194
|
+
const { command } = args;
|
|
1195
|
+
const bashOptions = fs ? { fs } : {};
|
|
1196
|
+
const bash = new justBash.Bash(bashOptions);
|
|
1197
|
+
try {
|
|
1198
|
+
const { exitCode, stderr, stdout } = await bash.exec(command);
|
|
1199
|
+
const bashExecOut = { exitCode, stderr, stdout };
|
|
1200
|
+
return {
|
|
1201
|
+
content: `Exit code: ${exitCode}
|
|
1202
|
+
|
|
1203
|
+
stdout:
|
|
1204
|
+
${stdout}
|
|
1205
|
+
|
|
1206
|
+
stderr:
|
|
1207
|
+
${stderr}`,
|
|
1208
|
+
result: bashExecOut
|
|
1209
|
+
};
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
1212
|
+
return {
|
|
1213
|
+
content: `Error executing bash command: ${err.message}`,
|
|
1214
|
+
result: null
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
// src/lib/fs.ts
|
|
1220
|
+
var basename = (path, separator) => {
|
|
1221
|
+
if (path[path.length - 1] === separator) path = path.slice(0, -1);
|
|
1222
|
+
const lastSlashIndex = path.lastIndexOf(separator);
|
|
1223
|
+
return lastSlashIndex === -1 ? path : path.slice(lastSlashIndex + 1);
|
|
1224
|
+
};
|
|
1225
|
+
var printTree = async (tab = "", children) => {
|
|
1226
|
+
let str = "";
|
|
1227
|
+
let last = children.length - 1;
|
|
1228
|
+
for (; last >= 0; last--) if (children[last]) break;
|
|
1229
|
+
for (let i = 0; i <= last; i++) {
|
|
1230
|
+
const fn = children[i];
|
|
1231
|
+
if (!fn) continue;
|
|
1232
|
+
const isLast = i === last;
|
|
1233
|
+
const child = await fn(tab + (isLast ? " " : "\u2502") + " ");
|
|
1234
|
+
const branch = child ? isLast ? "\u2514\u2500" : "\u251C\u2500" : "\u2502";
|
|
1235
|
+
str += "\n" + tab + branch + (child ? " " + child : "");
|
|
1236
|
+
}
|
|
1237
|
+
return str;
|
|
1238
|
+
};
|
|
1239
|
+
var toTree = async (fs, opts = {}) => {
|
|
1240
|
+
const separator = opts.separator || "/";
|
|
1241
|
+
let dir = opts.dir || separator;
|
|
1242
|
+
if (dir[dir.length - 1] !== separator) dir += separator;
|
|
1243
|
+
const tab = opts.tab || "";
|
|
1244
|
+
const depth = opts.depth ?? 10;
|
|
1245
|
+
const sort = opts.sort ?? true;
|
|
1246
|
+
let subtree = " (...)";
|
|
1247
|
+
if (depth > 0) {
|
|
1248
|
+
const list = await fs.readdirWithFileTypes?.(dir) || [];
|
|
1249
|
+
if (sort) {
|
|
1250
|
+
list.sort((a, b) => {
|
|
1251
|
+
if (a.isDirectory && b.isDirectory) {
|
|
1252
|
+
return a.name.toString().localeCompare(b.name.toString());
|
|
1253
|
+
} else if (a.isDirectory) {
|
|
1254
|
+
return -1;
|
|
1255
|
+
} else if (b.isDirectory) {
|
|
1256
|
+
return 1;
|
|
1257
|
+
} else {
|
|
1258
|
+
return a.name.toString().localeCompare(b.name.toString());
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
subtree = await printTree(
|
|
1263
|
+
tab,
|
|
1264
|
+
list.map((entry) => async (tab2) => {
|
|
1265
|
+
if (entry.isDirectory) {
|
|
1266
|
+
return toTree(fs, {
|
|
1267
|
+
dir: dir + entry.name,
|
|
1268
|
+
depth: depth - 1,
|
|
1269
|
+
tab: tab2
|
|
1270
|
+
});
|
|
1271
|
+
} else if (entry.isSymbolicLink) {
|
|
1272
|
+
return "" + entry.name + " \u2192 " + await fs.readlink(dir + entry.name);
|
|
1273
|
+
} else {
|
|
1274
|
+
return "" + entry.name;
|
|
1275
|
+
}
|
|
1276
|
+
})
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
const base = basename(dir, separator) + separator;
|
|
1280
|
+
return base + subtree;
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1283
|
+
exports.AGENT_HANDLER_NAMES = AGENT_HANDLER_NAMES;
|
|
1284
|
+
exports.ZeitlichPlugin = ZeitlichPlugin;
|
|
1285
|
+
exports.askUserQuestionTool = askUserQuestionTool;
|
|
1286
|
+
exports.bashTool = bashTool;
|
|
1287
|
+
exports.createAgentStateManager = createAgentStateManager;
|
|
1288
|
+
exports.createSession = createSession;
|
|
1289
|
+
exports.createSharedActivities = createSharedActivities;
|
|
1290
|
+
exports.createTaskCreateHandler = createTaskCreateHandler;
|
|
1291
|
+
exports.createTaskGetHandler = createTaskGetHandler;
|
|
1292
|
+
exports.createTaskListHandler = createTaskListHandler;
|
|
1293
|
+
exports.createTaskTool = createTaskTool;
|
|
1294
|
+
exports.createTaskUpdateHandler = createTaskUpdateHandler;
|
|
1295
|
+
exports.createToolRouter = createToolRouter;
|
|
1296
|
+
exports.editHandler = editHandler;
|
|
1297
|
+
exports.editTool = editTool;
|
|
1298
|
+
exports.globHandler = globHandler;
|
|
1299
|
+
exports.globTool = globTool;
|
|
1300
|
+
exports.grepTool = grepTool;
|
|
1301
|
+
exports.handleAskUserQuestionToolResult = handleAskUserQuestionToolResult;
|
|
1302
|
+
exports.handleBashTool = handleBashTool;
|
|
1303
|
+
exports.hasNoOtherToolCalls = hasNoOtherToolCalls;
|
|
1304
|
+
exports.invokeModel = invokeModel;
|
|
1305
|
+
exports.isTerminalStatus = isTerminalStatus;
|
|
1306
|
+
exports.readTool = readTool;
|
|
1307
|
+
exports.taskCreateTool = taskCreateTool;
|
|
1308
|
+
exports.taskGetTool = taskGetTool;
|
|
1309
|
+
exports.taskListTool = taskListTool;
|
|
1310
|
+
exports.taskUpdateTool = taskUpdateTool;
|
|
1311
|
+
exports.toTree = toTree;
|
|
1312
|
+
exports.writeTool = writeTool;
|
|
1313
|
+
//# sourceMappingURL=index.cjs.map
|
|
1314
|
+
//# sourceMappingURL=index.cjs.map
|