phi-code-agent 0.56.3 → 0.74.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 +109 -33
- package/dist/agent-loop.d.ts +3 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +298 -127
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent.d.ts +88 -127
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +323 -318
- package/dist/agent.js.map +1 -1
- package/dist/harness/agent-harness.d.ts +85 -0
- package/dist/harness/agent-harness.d.ts.map +1 -0
- package/dist/harness/agent-harness.js +728 -0
- package/dist/harness/agent-harness.js.map +1 -0
- package/dist/harness/compaction/branch-summarization.d.ts +88 -0
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/harness/compaction/branch-summarization.js +243 -0
- package/dist/harness/compaction/branch-summarization.js.map +1 -0
- package/dist/harness/compaction/compaction.d.ts +122 -0
- package/dist/harness/compaction/compaction.d.ts.map +1 -0
- package/dist/harness/compaction/compaction.js +616 -0
- package/dist/harness/compaction/compaction.js.map +1 -0
- package/dist/harness/compaction/utils.d.ts +38 -0
- package/dist/harness/compaction/utils.d.ts.map +1 -0
- package/dist/harness/compaction/utils.js +153 -0
- package/dist/harness/compaction/utils.js.map +1 -0
- package/dist/harness/env/nodejs.d.ts +44 -0
- package/dist/harness/env/nodejs.d.ts.map +1 -0
- package/dist/harness/env/nodejs.js +348 -0
- package/dist/harness/env/nodejs.js.map +1 -0
- package/dist/harness/execution-env.d.ts +4 -0
- package/dist/harness/execution-env.d.ts.map +1 -0
- package/dist/harness/execution-env.js +3 -0
- package/dist/harness/execution-env.js.map +1 -0
- package/dist/harness/messages.d.ts +51 -0
- package/dist/harness/messages.d.ts.map +1 -0
- package/dist/harness/messages.js +102 -0
- package/dist/harness/messages.js.map +1 -0
- package/dist/harness/prompt-templates.d.ts +45 -0
- package/dist/harness/prompt-templates.d.ts.map +1 -0
- package/dist/harness/prompt-templates.js +200 -0
- package/dist/harness/prompt-templates.js.map +1 -0
- package/dist/harness/session/repo/jsonl.d.ts +20 -0
- package/dist/harness/session/repo/jsonl.d.ts.map +1 -0
- package/dist/harness/session/repo/jsonl.js +92 -0
- package/dist/harness/session/repo/jsonl.js.map +1 -0
- package/dist/harness/session/repo/memory.d.ts +18 -0
- package/dist/harness/session/repo/memory.d.ts.map +1 -0
- package/dist/harness/session/repo/memory.js +42 -0
- package/dist/harness/session/repo/memory.js.map +1 -0
- package/dist/harness/session/repo/shared.d.ts +10 -0
- package/dist/harness/session/repo/shared.d.ts.map +1 -0
- package/dist/harness/session/repo/shared.js +31 -0
- package/dist/harness/session/repo/shared.js.map +1 -0
- package/dist/harness/session/session.d.ts +32 -0
- package/dist/harness/session/session.d.ts.map +1 -0
- package/dist/harness/session/session.js +196 -0
- package/dist/harness/session/session.js.map +1 -0
- package/dist/harness/session/storage/jsonl.d.ts +30 -0
- package/dist/harness/session/storage/jsonl.d.ts.map +1 -0
- package/dist/harness/session/storage/jsonl.js +170 -0
- package/dist/harness/session/storage/jsonl.js.map +1 -0
- package/dist/harness/session/storage/memory.d.ts +26 -0
- package/dist/harness/session/storage/memory.d.ts.map +1 -0
- package/dist/harness/session/storage/memory.js +90 -0
- package/dist/harness/session/storage/memory.js.map +1 -0
- package/dist/harness/skills.d.ts +41 -0
- package/dist/harness/skills.d.ts.map +1 -0
- package/dist/harness/skills.js +259 -0
- package/dist/harness/skills.js.map +1 -0
- package/dist/harness/system-prompt.d.ts +3 -0
- package/dist/harness/system-prompt.d.ts.map +1 -0
- package/dist/harness/system-prompt.js +30 -0
- package/dist/harness/system-prompt.js.map +1 -0
- package/dist/harness/types.d.ts +525 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +16 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/utils/shell-output.d.ts +14 -0
- package/dist/harness/utils/shell-output.d.ts.map +1 -0
- package/dist/harness/utils/shell-output.js +97 -0
- package/dist/harness/utils/shell-output.js.map +1 -0
- package/dist/harness/utils/truncate.d.ts +70 -0
- package/dist/harness/utils/truncate.d.ts.map +1 -0
- package/dist/harness/utils/truncate.js +205 -0
- package/dist/harness/utils/truncate.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +4 -20
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +15 -5
- package/dist/proxy.js.map +1 -1
- package/dist/types.d.ts +224 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -3
package/dist/agent-loop.js
CHANGED
|
@@ -9,20 +9,11 @@ import { EventStream, streamSimple, validateToolArguments, } from "phi-code-ai";
|
|
|
9
9
|
*/
|
|
10
10
|
export function agentLoop(prompts, context, config, signal, streamFn) {
|
|
11
11
|
const stream = createAgentStream();
|
|
12
|
-
(async () => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
stream.push({ type: "agent_start" });
|
|
19
|
-
stream.push({ type: "turn_start" });
|
|
20
|
-
for (const prompt of prompts) {
|
|
21
|
-
stream.push({ type: "message_start", message: prompt });
|
|
22
|
-
stream.push({ type: "message_end", message: prompt });
|
|
23
|
-
}
|
|
24
|
-
await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
|
|
25
|
-
})();
|
|
12
|
+
void runAgentLoop(prompts, context, config, async (event) => {
|
|
13
|
+
stream.push(event);
|
|
14
|
+
}, signal, streamFn).then((messages) => {
|
|
15
|
+
stream.end(messages);
|
|
16
|
+
});
|
|
26
17
|
return stream;
|
|
27
18
|
}
|
|
28
19
|
/**
|
|
@@ -41,33 +32,61 @@ export function agentLoopContinue(context, config, signal, streamFn) {
|
|
|
41
32
|
throw new Error("Cannot continue from message role: assistant");
|
|
42
33
|
}
|
|
43
34
|
const stream = createAgentStream();
|
|
44
|
-
(async () => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
stream.
|
|
48
|
-
|
|
49
|
-
await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
|
|
50
|
-
})();
|
|
35
|
+
void runAgentLoopContinue(context, config, async (event) => {
|
|
36
|
+
stream.push(event);
|
|
37
|
+
}, signal, streamFn).then((messages) => {
|
|
38
|
+
stream.end(messages);
|
|
39
|
+
});
|
|
51
40
|
return stream;
|
|
52
41
|
}
|
|
42
|
+
export async function runAgentLoop(prompts, context, config, emit, signal, streamFn) {
|
|
43
|
+
const newMessages = [...prompts];
|
|
44
|
+
const currentContext = {
|
|
45
|
+
...context,
|
|
46
|
+
messages: [...context.messages, ...prompts],
|
|
47
|
+
};
|
|
48
|
+
await emit({ type: "agent_start" });
|
|
49
|
+
await emit({ type: "turn_start" });
|
|
50
|
+
for (const prompt of prompts) {
|
|
51
|
+
await emit({ type: "message_start", message: prompt });
|
|
52
|
+
await emit({ type: "message_end", message: prompt });
|
|
53
|
+
}
|
|
54
|
+
await runLoop(currentContext, newMessages, config, signal, emit, streamFn);
|
|
55
|
+
return newMessages;
|
|
56
|
+
}
|
|
57
|
+
export async function runAgentLoopContinue(context, config, emit, signal, streamFn) {
|
|
58
|
+
if (context.messages.length === 0) {
|
|
59
|
+
throw new Error("Cannot continue: no messages in context");
|
|
60
|
+
}
|
|
61
|
+
if (context.messages[context.messages.length - 1].role === "assistant") {
|
|
62
|
+
throw new Error("Cannot continue from message role: assistant");
|
|
63
|
+
}
|
|
64
|
+
const newMessages = [];
|
|
65
|
+
const currentContext = { ...context };
|
|
66
|
+
await emit({ type: "agent_start" });
|
|
67
|
+
await emit({ type: "turn_start" });
|
|
68
|
+
await runLoop(currentContext, newMessages, config, signal, emit, streamFn);
|
|
69
|
+
return newMessages;
|
|
70
|
+
}
|
|
53
71
|
function createAgentStream() {
|
|
54
72
|
return new EventStream((event) => event.type === "agent_end", (event) => (event.type === "agent_end" ? event.messages : []));
|
|
55
73
|
}
|
|
56
74
|
/**
|
|
57
75
|
* Main loop logic shared by agentLoop and agentLoopContinue.
|
|
58
76
|
*/
|
|
59
|
-
async function runLoop(
|
|
77
|
+
async function runLoop(initialContext, newMessages, initialConfig, signal, emit, streamFn) {
|
|
78
|
+
let currentContext = initialContext;
|
|
79
|
+
let config = initialConfig;
|
|
60
80
|
let firstTurn = true;
|
|
61
81
|
// Check for steering messages at start (user may have typed while waiting)
|
|
62
82
|
let pendingMessages = (await config.getSteeringMessages?.()) || [];
|
|
63
83
|
// Outer loop: continues when queued follow-up messages arrive after agent would stop
|
|
64
84
|
while (true) {
|
|
65
85
|
let hasMoreToolCalls = true;
|
|
66
|
-
let steeringAfterTools = null;
|
|
67
86
|
// Inner loop: process tool calls and steering messages
|
|
68
87
|
while (hasMoreToolCalls || pendingMessages.length > 0) {
|
|
69
88
|
if (!firstTurn) {
|
|
70
|
-
|
|
89
|
+
await emit({ type: "turn_start" });
|
|
71
90
|
}
|
|
72
91
|
else {
|
|
73
92
|
firstTurn = false;
|
|
@@ -75,44 +94,64 @@ async function runLoop(currentContext, newMessages, config, signal, stream, stre
|
|
|
75
94
|
// Process pending messages (inject before next assistant response)
|
|
76
95
|
if (pendingMessages.length > 0) {
|
|
77
96
|
for (const message of pendingMessages) {
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
await emit({ type: "message_start", message });
|
|
98
|
+
await emit({ type: "message_end", message });
|
|
80
99
|
currentContext.messages.push(message);
|
|
81
100
|
newMessages.push(message);
|
|
82
101
|
}
|
|
83
102
|
pendingMessages = [];
|
|
84
103
|
}
|
|
85
104
|
// Stream assistant response
|
|
86
|
-
const message = await streamAssistantResponse(currentContext, config, signal,
|
|
105
|
+
const message = await streamAssistantResponse(currentContext, config, signal, emit, streamFn);
|
|
87
106
|
newMessages.push(message);
|
|
88
107
|
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
stream.end(newMessages);
|
|
108
|
+
await emit({ type: "turn_end", message, toolResults: [] });
|
|
109
|
+
await emit({ type: "agent_end", messages: newMessages });
|
|
92
110
|
return;
|
|
93
111
|
}
|
|
94
112
|
// Check for tool calls
|
|
95
113
|
const toolCalls = message.content.filter((c) => c.type === "toolCall");
|
|
96
|
-
hasMoreToolCalls = toolCalls.length > 0;
|
|
97
114
|
const toolResults = [];
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
hasMoreToolCalls = false;
|
|
116
|
+
if (toolCalls.length > 0) {
|
|
117
|
+
const executedToolBatch = await executeToolCalls(currentContext, message, config, signal, emit);
|
|
118
|
+
toolResults.push(...executedToolBatch.messages);
|
|
119
|
+
hasMoreToolCalls = !executedToolBatch.terminate;
|
|
102
120
|
for (const result of toolResults) {
|
|
103
121
|
currentContext.messages.push(result);
|
|
104
122
|
newMessages.push(result);
|
|
105
123
|
}
|
|
106
124
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
125
|
+
await emit({ type: "turn_end", message, toolResults });
|
|
126
|
+
const nextTurnContext = {
|
|
127
|
+
message,
|
|
128
|
+
toolResults,
|
|
129
|
+
context: currentContext,
|
|
130
|
+
newMessages,
|
|
131
|
+
};
|
|
132
|
+
const nextTurnSnapshot = await config.prepareNextTurn?.(nextTurnContext);
|
|
133
|
+
if (nextTurnSnapshot) {
|
|
134
|
+
currentContext = nextTurnSnapshot.context ?? currentContext;
|
|
135
|
+
config = {
|
|
136
|
+
...config,
|
|
137
|
+
model: nextTurnSnapshot.model ?? config.model,
|
|
138
|
+
reasoning: nextTurnSnapshot.thinkingLevel === undefined
|
|
139
|
+
? config.reasoning
|
|
140
|
+
: nextTurnSnapshot.thinkingLevel === "off"
|
|
141
|
+
? undefined
|
|
142
|
+
: nextTurnSnapshot.thinkingLevel,
|
|
143
|
+
};
|
|
112
144
|
}
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
if (await config.shouldStopAfterTurn?.({
|
|
146
|
+
message,
|
|
147
|
+
toolResults,
|
|
148
|
+
context: currentContext,
|
|
149
|
+
newMessages,
|
|
150
|
+
})) {
|
|
151
|
+
await emit({ type: "agent_end", messages: newMessages });
|
|
152
|
+
return;
|
|
115
153
|
}
|
|
154
|
+
pendingMessages = (await config.getSteeringMessages?.()) || [];
|
|
116
155
|
}
|
|
117
156
|
// Agent would stop here. Check for follow-up messages.
|
|
118
157
|
const followUpMessages = (await config.getFollowUpMessages?.()) || [];
|
|
@@ -124,14 +163,13 @@ async function runLoop(currentContext, newMessages, config, signal, stream, stre
|
|
|
124
163
|
// No more messages, exit
|
|
125
164
|
break;
|
|
126
165
|
}
|
|
127
|
-
|
|
128
|
-
stream.end(newMessages);
|
|
166
|
+
await emit({ type: "agent_end", messages: newMessages });
|
|
129
167
|
}
|
|
130
168
|
/**
|
|
131
169
|
* Stream an assistant response from the LLM.
|
|
132
170
|
* This is where AgentMessage[] gets transformed to Message[] for the LLM.
|
|
133
171
|
*/
|
|
134
|
-
async function streamAssistantResponse(context, config, signal,
|
|
172
|
+
async function streamAssistantResponse(context, config, signal, emit, streamFn) {
|
|
135
173
|
// Apply context transform if configured (AgentMessage[] → AgentMessage[])
|
|
136
174
|
let messages = context.messages;
|
|
137
175
|
if (config.transformContext) {
|
|
@@ -161,7 +199,7 @@ async function streamAssistantResponse(context, config, signal, stream, streamFn
|
|
|
161
199
|
partialMessage = event.partial;
|
|
162
200
|
context.messages.push(partialMessage);
|
|
163
201
|
addedPartial = true;
|
|
164
|
-
|
|
202
|
+
await emit({ type: "message_start", message: { ...partialMessage } });
|
|
165
203
|
break;
|
|
166
204
|
case "text_start":
|
|
167
205
|
case "text_delta":
|
|
@@ -175,7 +213,7 @@ async function streamAssistantResponse(context, config, signal, stream, streamFn
|
|
|
175
213
|
if (partialMessage) {
|
|
176
214
|
partialMessage = event.partial;
|
|
177
215
|
context.messages[context.messages.length - 1] = partialMessage;
|
|
178
|
-
|
|
216
|
+
await emit({
|
|
179
217
|
type: "message_update",
|
|
180
218
|
assistantMessageEvent: event,
|
|
181
219
|
message: { ...partialMessage },
|
|
@@ -192,117 +230,250 @@ async function streamAssistantResponse(context, config, signal, stream, streamFn
|
|
|
192
230
|
context.messages.push(finalMessage);
|
|
193
231
|
}
|
|
194
232
|
if (!addedPartial) {
|
|
195
|
-
|
|
233
|
+
await emit({ type: "message_start", message: { ...finalMessage } });
|
|
196
234
|
}
|
|
197
|
-
|
|
235
|
+
await emit({ type: "message_end", message: finalMessage });
|
|
198
236
|
return finalMessage;
|
|
199
237
|
}
|
|
200
238
|
}
|
|
201
239
|
}
|
|
202
|
-
|
|
240
|
+
const finalMessage = await response.result();
|
|
241
|
+
if (addedPartial) {
|
|
242
|
+
context.messages[context.messages.length - 1] = finalMessage;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
context.messages.push(finalMessage);
|
|
246
|
+
await emit({ type: "message_start", message: { ...finalMessage } });
|
|
247
|
+
}
|
|
248
|
+
await emit({ type: "message_end", message: finalMessage });
|
|
249
|
+
return finalMessage;
|
|
203
250
|
}
|
|
204
251
|
/**
|
|
205
252
|
* Execute tool calls from an assistant message.
|
|
206
253
|
*/
|
|
207
|
-
async function executeToolCalls(
|
|
254
|
+
async function executeToolCalls(currentContext, assistantMessage, config, signal, emit) {
|
|
208
255
|
const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall");
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
256
|
+
const hasSequentialToolCall = toolCalls.some((tc) => currentContext.tools?.find((t) => t.name === tc.name)?.executionMode === "sequential");
|
|
257
|
+
if (config.toolExecution === "sequential" || hasSequentialToolCall) {
|
|
258
|
+
return executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, signal, emit);
|
|
259
|
+
}
|
|
260
|
+
return executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, signal, emit);
|
|
261
|
+
}
|
|
262
|
+
async function executeToolCallsSequential(currentContext, assistantMessage, toolCalls, config, signal, emit) {
|
|
263
|
+
const finalizedCalls = [];
|
|
264
|
+
const messages = [];
|
|
265
|
+
for (const toolCall of toolCalls) {
|
|
266
|
+
await emit({
|
|
215
267
|
type: "tool_execution_start",
|
|
216
268
|
toolCallId: toolCall.id,
|
|
217
269
|
toolName: toolCall.name,
|
|
218
270
|
args: toolCall.arguments,
|
|
219
271
|
});
|
|
220
|
-
|
|
221
|
-
let
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
stream.push({
|
|
228
|
-
type: "tool_execution_update",
|
|
229
|
-
toolCallId: toolCall.id,
|
|
230
|
-
toolName: toolCall.name,
|
|
231
|
-
args: toolCall.arguments,
|
|
232
|
-
partialResult,
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
catch (e) {
|
|
237
|
-
result = {
|
|
238
|
-
content: [{ type: "text", text: e instanceof Error ? e.message : String(e) }],
|
|
239
|
-
details: {},
|
|
272
|
+
const preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);
|
|
273
|
+
let finalized;
|
|
274
|
+
if (preparation.kind === "immediate") {
|
|
275
|
+
finalized = {
|
|
276
|
+
toolCall,
|
|
277
|
+
result: preparation.result,
|
|
278
|
+
isError: preparation.isError,
|
|
240
279
|
};
|
|
241
|
-
isError = true;
|
|
242
280
|
}
|
|
243
|
-
|
|
244
|
-
|
|
281
|
+
else {
|
|
282
|
+
const executed = await executePreparedToolCall(preparation, signal, emit);
|
|
283
|
+
finalized = await finalizeExecutedToolCall(currentContext, assistantMessage, preparation, executed, config, signal);
|
|
284
|
+
}
|
|
285
|
+
await emitToolExecutionEnd(finalized, emit);
|
|
286
|
+
const toolResultMessage = createToolResultMessage(finalized);
|
|
287
|
+
await emitToolResultMessage(toolResultMessage, emit);
|
|
288
|
+
finalizedCalls.push(finalized);
|
|
289
|
+
messages.push(toolResultMessage);
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
messages,
|
|
293
|
+
terminate: shouldTerminateToolBatch(finalizedCalls),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async function executeToolCallsParallel(currentContext, assistantMessage, toolCalls, config, signal, emit) {
|
|
297
|
+
const finalizedCalls = [];
|
|
298
|
+
for (const toolCall of toolCalls) {
|
|
299
|
+
await emit({
|
|
300
|
+
type: "tool_execution_start",
|
|
245
301
|
toolCallId: toolCall.id,
|
|
246
302
|
toolName: toolCall.name,
|
|
247
|
-
|
|
248
|
-
isError,
|
|
303
|
+
args: toolCall.arguments,
|
|
249
304
|
});
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
305
|
+
const preparation = await prepareToolCall(currentContext, assistantMessage, toolCall, config, signal);
|
|
306
|
+
if (preparation.kind === "immediate") {
|
|
307
|
+
const finalized = {
|
|
308
|
+
toolCall,
|
|
309
|
+
result: preparation.result,
|
|
310
|
+
isError: preparation.isError,
|
|
311
|
+
};
|
|
312
|
+
await emitToolExecutionEnd(finalized, emit);
|
|
313
|
+
finalizedCalls.push(finalized);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
finalizedCalls.push(async () => {
|
|
317
|
+
const executed = await executePreparedToolCall(preparation, signal, emit);
|
|
318
|
+
const finalized = await finalizeExecutedToolCall(currentContext, assistantMessage, preparation, executed, config, signal);
|
|
319
|
+
await emitToolExecutionEnd(finalized, emit);
|
|
320
|
+
return finalized;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
const orderedFinalizedCalls = await Promise.all(finalizedCalls.map((entry) => (typeof entry === "function" ? entry() : Promise.resolve(entry))));
|
|
324
|
+
const messages = [];
|
|
325
|
+
for (const finalized of orderedFinalizedCalls) {
|
|
326
|
+
const toolResultMessage = createToolResultMessage(finalized);
|
|
327
|
+
await emitToolResultMessage(toolResultMessage, emit);
|
|
328
|
+
messages.push(toolResultMessage);
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
messages,
|
|
332
|
+
terminate: shouldTerminateToolBatch(orderedFinalizedCalls),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function shouldTerminateToolBatch(finalizedCalls) {
|
|
336
|
+
return finalizedCalls.length > 0 && finalizedCalls.every((finalized) => finalized.result.terminate === true);
|
|
337
|
+
}
|
|
338
|
+
function prepareToolCallArguments(tool, toolCall) {
|
|
339
|
+
if (!tool.prepareArguments) {
|
|
340
|
+
return toolCall;
|
|
341
|
+
}
|
|
342
|
+
const preparedArguments = tool.prepareArguments(toolCall.arguments);
|
|
343
|
+
if (preparedArguments === toolCall.arguments) {
|
|
344
|
+
return toolCall;
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
...toolCall,
|
|
348
|
+
arguments: preparedArguments,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
async function prepareToolCall(currentContext, assistantMessage, toolCall, config, signal) {
|
|
352
|
+
const tool = currentContext.tools?.find((t) => t.name === toolCall.name);
|
|
353
|
+
if (!tool) {
|
|
354
|
+
return {
|
|
355
|
+
kind: "immediate",
|
|
356
|
+
result: createErrorToolResult(`Tool ${toolCall.name} not found`),
|
|
357
|
+
isError: true,
|
|
258
358
|
};
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
const preparedToolCall = prepareToolCallArguments(tool, toolCall);
|
|
362
|
+
const validatedArgs = validateToolArguments(tool, preparedToolCall);
|
|
363
|
+
if (config.beforeToolCall) {
|
|
364
|
+
const beforeResult = await config.beforeToolCall({
|
|
365
|
+
assistantMessage,
|
|
366
|
+
toolCall,
|
|
367
|
+
args: validatedArgs,
|
|
368
|
+
context: currentContext,
|
|
369
|
+
}, signal);
|
|
370
|
+
if (beforeResult?.block) {
|
|
371
|
+
return {
|
|
372
|
+
kind: "immediate",
|
|
373
|
+
result: createErrorToolResult(beforeResult.reason || "Tool execution was blocked"),
|
|
374
|
+
isError: true,
|
|
375
|
+
};
|
|
272
376
|
}
|
|
273
377
|
}
|
|
378
|
+
return {
|
|
379
|
+
kind: "prepared",
|
|
380
|
+
toolCall,
|
|
381
|
+
tool,
|
|
382
|
+
args: validatedArgs,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
return {
|
|
387
|
+
kind: "immediate",
|
|
388
|
+
result: createErrorToolResult(error instanceof Error ? error.message : String(error)),
|
|
389
|
+
isError: true,
|
|
390
|
+
};
|
|
274
391
|
}
|
|
275
|
-
return { toolResults: results, steeringMessages };
|
|
276
392
|
}
|
|
277
|
-
function
|
|
278
|
-
const
|
|
279
|
-
|
|
393
|
+
async function executePreparedToolCall(prepared, signal, emit) {
|
|
394
|
+
const updateEvents = [];
|
|
395
|
+
try {
|
|
396
|
+
const result = await prepared.tool.execute(prepared.toolCall.id, prepared.args, signal, (partialResult) => {
|
|
397
|
+
updateEvents.push(Promise.resolve(emit({
|
|
398
|
+
type: "tool_execution_update",
|
|
399
|
+
toolCallId: prepared.toolCall.id,
|
|
400
|
+
toolName: prepared.toolCall.name,
|
|
401
|
+
args: prepared.toolCall.arguments,
|
|
402
|
+
partialResult,
|
|
403
|
+
})));
|
|
404
|
+
});
|
|
405
|
+
await Promise.all(updateEvents);
|
|
406
|
+
return { result, isError: false };
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
await Promise.all(updateEvents);
|
|
410
|
+
return {
|
|
411
|
+
result: createErrorToolResult(error instanceof Error ? error.message : String(error)),
|
|
412
|
+
isError: true,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async function finalizeExecutedToolCall(currentContext, assistantMessage, prepared, executed, config, signal) {
|
|
417
|
+
let result = executed.result;
|
|
418
|
+
let isError = executed.isError;
|
|
419
|
+
if (config.afterToolCall) {
|
|
420
|
+
try {
|
|
421
|
+
const afterResult = await config.afterToolCall({
|
|
422
|
+
assistantMessage,
|
|
423
|
+
toolCall: prepared.toolCall,
|
|
424
|
+
args: prepared.args,
|
|
425
|
+
result,
|
|
426
|
+
isError,
|
|
427
|
+
context: currentContext,
|
|
428
|
+
}, signal);
|
|
429
|
+
if (afterResult) {
|
|
430
|
+
result = {
|
|
431
|
+
content: afterResult.content ?? result.content,
|
|
432
|
+
details: afterResult.details ?? result.details,
|
|
433
|
+
terminate: afterResult.terminate ?? result.terminate,
|
|
434
|
+
};
|
|
435
|
+
isError = afterResult.isError ?? isError;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
result = createErrorToolResult(error instanceof Error ? error.message : String(error));
|
|
440
|
+
isError = true;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
toolCall: prepared.toolCall,
|
|
445
|
+
result,
|
|
446
|
+
isError,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function createErrorToolResult(message) {
|
|
450
|
+
return {
|
|
451
|
+
content: [{ type: "text", text: message }],
|
|
280
452
|
details: {},
|
|
281
453
|
};
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
toolName: toolCall.name,
|
|
286
|
-
args: toolCall.arguments,
|
|
287
|
-
});
|
|
288
|
-
stream.push({
|
|
454
|
+
}
|
|
455
|
+
async function emitToolExecutionEnd(finalized, emit) {
|
|
456
|
+
await emit({
|
|
289
457
|
type: "tool_execution_end",
|
|
290
|
-
toolCallId: toolCall.id,
|
|
291
|
-
toolName: toolCall.name,
|
|
292
|
-
result,
|
|
293
|
-
isError:
|
|
458
|
+
toolCallId: finalized.toolCall.id,
|
|
459
|
+
toolName: finalized.toolCall.name,
|
|
460
|
+
result: finalized.result,
|
|
461
|
+
isError: finalized.isError,
|
|
294
462
|
});
|
|
295
|
-
|
|
463
|
+
}
|
|
464
|
+
function createToolResultMessage(finalized) {
|
|
465
|
+
return {
|
|
296
466
|
role: "toolResult",
|
|
297
|
-
toolCallId: toolCall.id,
|
|
298
|
-
toolName: toolCall.name,
|
|
299
|
-
content: result.content,
|
|
300
|
-
details:
|
|
301
|
-
isError:
|
|
467
|
+
toolCallId: finalized.toolCall.id,
|
|
468
|
+
toolName: finalized.toolCall.name,
|
|
469
|
+
content: finalized.result.content,
|
|
470
|
+
details: finalized.result.details,
|
|
471
|
+
isError: finalized.isError,
|
|
302
472
|
timestamp: Date.now(),
|
|
303
473
|
};
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
474
|
+
}
|
|
475
|
+
async function emitToolResultMessage(toolResultMessage, emit) {
|
|
476
|
+
await emit({ type: "message_start", message: toolResultMessage });
|
|
477
|
+
await emit({ type: "message_end", message: toolResultMessage });
|
|
307
478
|
}
|
|
308
479
|
//# sourceMappingURL=agent-loop.js.map
|