phi-code-agent 0.56.3 → 0.74.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +109 -33
  2. package/dist/agent-loop.d.ts +3 -0
  3. package/dist/agent-loop.d.ts.map +1 -1
  4. package/dist/agent-loop.js +298 -127
  5. package/dist/agent-loop.js.map +1 -1
  6. package/dist/agent.d.ts +88 -127
  7. package/dist/agent.d.ts.map +1 -1
  8. package/dist/agent.js +323 -318
  9. package/dist/agent.js.map +1 -1
  10. package/dist/harness/agent-harness.d.ts +85 -0
  11. package/dist/harness/agent-harness.d.ts.map +1 -0
  12. package/dist/harness/agent-harness.js +728 -0
  13. package/dist/harness/agent-harness.js.map +1 -0
  14. package/dist/harness/compaction/branch-summarization.d.ts +88 -0
  15. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
  16. package/dist/harness/compaction/branch-summarization.js +243 -0
  17. package/dist/harness/compaction/branch-summarization.js.map +1 -0
  18. package/dist/harness/compaction/compaction.d.ts +122 -0
  19. package/dist/harness/compaction/compaction.d.ts.map +1 -0
  20. package/dist/harness/compaction/compaction.js +632 -0
  21. package/dist/harness/compaction/compaction.js.map +1 -0
  22. package/dist/harness/compaction/utils.d.ts +38 -0
  23. package/dist/harness/compaction/utils.d.ts.map +1 -0
  24. package/dist/harness/compaction/utils.js +153 -0
  25. package/dist/harness/compaction/utils.js.map +1 -0
  26. package/dist/harness/env/nodejs.d.ts +44 -0
  27. package/dist/harness/env/nodejs.d.ts.map +1 -0
  28. package/dist/harness/env/nodejs.js +348 -0
  29. package/dist/harness/env/nodejs.js.map +1 -0
  30. package/dist/harness/execution-env.d.ts +4 -0
  31. package/dist/harness/execution-env.d.ts.map +1 -0
  32. package/dist/harness/execution-env.js +3 -0
  33. package/dist/harness/execution-env.js.map +1 -0
  34. package/dist/harness/messages.d.ts +51 -0
  35. package/dist/harness/messages.d.ts.map +1 -0
  36. package/dist/harness/messages.js +102 -0
  37. package/dist/harness/messages.js.map +1 -0
  38. package/dist/harness/prompt-templates.d.ts +45 -0
  39. package/dist/harness/prompt-templates.d.ts.map +1 -0
  40. package/dist/harness/prompt-templates.js +200 -0
  41. package/dist/harness/prompt-templates.js.map +1 -0
  42. package/dist/harness/session/repo/jsonl.d.ts +20 -0
  43. package/dist/harness/session/repo/jsonl.d.ts.map +1 -0
  44. package/dist/harness/session/repo/jsonl.js +92 -0
  45. package/dist/harness/session/repo/jsonl.js.map +1 -0
  46. package/dist/harness/session/repo/memory.d.ts +18 -0
  47. package/dist/harness/session/repo/memory.d.ts.map +1 -0
  48. package/dist/harness/session/repo/memory.js +42 -0
  49. package/dist/harness/session/repo/memory.js.map +1 -0
  50. package/dist/harness/session/repo/shared.d.ts +10 -0
  51. package/dist/harness/session/repo/shared.d.ts.map +1 -0
  52. package/dist/harness/session/repo/shared.js +31 -0
  53. package/dist/harness/session/repo/shared.js.map +1 -0
  54. package/dist/harness/session/session.d.ts +32 -0
  55. package/dist/harness/session/session.d.ts.map +1 -0
  56. package/dist/harness/session/session.js +196 -0
  57. package/dist/harness/session/session.js.map +1 -0
  58. package/dist/harness/session/storage/jsonl.d.ts +30 -0
  59. package/dist/harness/session/storage/jsonl.d.ts.map +1 -0
  60. package/dist/harness/session/storage/jsonl.js +171 -0
  61. package/dist/harness/session/storage/jsonl.js.map +1 -0
  62. package/dist/harness/session/storage/memory.d.ts +26 -0
  63. package/dist/harness/session/storage/memory.d.ts.map +1 -0
  64. package/dist/harness/session/storage/memory.js +90 -0
  65. package/dist/harness/session/storage/memory.js.map +1 -0
  66. package/dist/harness/skills.d.ts +41 -0
  67. package/dist/harness/skills.d.ts.map +1 -0
  68. package/dist/harness/skills.js +259 -0
  69. package/dist/harness/skills.js.map +1 -0
  70. package/dist/harness/system-prompt.d.ts +3 -0
  71. package/dist/harness/system-prompt.d.ts.map +1 -0
  72. package/dist/harness/system-prompt.js +30 -0
  73. package/dist/harness/system-prompt.js.map +1 -0
  74. package/dist/harness/types.d.ts +525 -0
  75. package/dist/harness/types.d.ts.map +1 -0
  76. package/dist/harness/types.js +16 -0
  77. package/dist/harness/types.js.map +1 -0
  78. package/dist/harness/utils/shell-output.d.ts +14 -0
  79. package/dist/harness/utils/shell-output.d.ts.map +1 -0
  80. package/dist/harness/utils/shell-output.js +97 -0
  81. package/dist/harness/utils/shell-output.js.map +1 -0
  82. package/dist/harness/utils/truncate.d.ts +70 -0
  83. package/dist/harness/utils/truncate.d.ts.map +1 -0
  84. package/dist/harness/utils/truncate.js +205 -0
  85. package/dist/harness/utils/truncate.js.map +1 -0
  86. package/dist/index.d.ts +15 -0
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +16 -0
  89. package/dist/index.js.map +1 -1
  90. package/dist/proxy.d.ts +4 -20
  91. package/dist/proxy.d.ts.map +1 -1
  92. package/dist/proxy.js +32 -5
  93. package/dist/proxy.js.map +1 -1
  94. package/dist/types.d.ts +224 -16
  95. package/dist/types.d.ts.map +1 -1
  96. package/dist/types.js.map +1 -1
  97. package/package.json +6 -3
@@ -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
- const newMessages = [...prompts];
14
- const currentContext = {
15
- ...context,
16
- messages: [...context.messages, ...prompts],
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
- const newMessages = [];
46
- const currentContext = { ...context };
47
- stream.push({ type: "agent_start" });
48
- stream.push({ type: "turn_start" });
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(currentContext, newMessages, config, signal, stream, streamFn) {
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
- stream.push({ type: "turn_start" });
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
- stream.push({ type: "message_start", message });
79
- stream.push({ type: "message_end", message });
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, stream, streamFn);
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
- stream.push({ type: "turn_end", message, toolResults: [] });
90
- stream.push({ type: "agent_end", messages: newMessages });
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
- if (hasMoreToolCalls) {
99
- const toolExecution = await executeToolCalls(currentContext.tools, message, signal, stream, config.getSteeringMessages);
100
- toolResults.push(...toolExecution.toolResults);
101
- steeringAfterTools = toolExecution.steeringMessages ?? null;
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
- stream.push({ type: "turn_end", message, toolResults });
108
- // Get steering messages after turn completes
109
- if (steeringAfterTools && steeringAfterTools.length > 0) {
110
- pendingMessages = steeringAfterTools;
111
- steeringAfterTools = null;
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
- else {
114
- pendingMessages = (await config.getSteeringMessages?.()) || [];
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
- stream.push({ type: "agent_end", messages: newMessages });
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, stream, streamFn) {
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
- stream.push({ type: "message_start", message: { ...partialMessage } });
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
- stream.push({
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
- stream.push({ type: "message_start", message: { ...finalMessage } });
233
+ await emit({ type: "message_start", message: { ...finalMessage } });
196
234
  }
197
- stream.push({ type: "message_end", message: finalMessage });
235
+ await emit({ type: "message_end", message: finalMessage });
198
236
  return finalMessage;
199
237
  }
200
238
  }
201
239
  }
202
- return await response.result();
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(tools, assistantMessage, signal, stream, getSteeringMessages) {
254
+ async function executeToolCalls(currentContext, assistantMessage, config, signal, emit) {
208
255
  const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall");
209
- const results = [];
210
- let steeringMessages;
211
- for (let index = 0; index < toolCalls.length; index++) {
212
- const toolCall = toolCalls[index];
213
- const tool = tools?.find((t) => t.name === toolCall.name);
214
- stream.push({
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
- let result;
221
- let isError = false;
222
- try {
223
- if (!tool)
224
- throw new Error(`Tool ${toolCall.name} not found`);
225
- const validatedArgs = validateToolArguments(tool, toolCall);
226
- result = await tool.execute(toolCall.id, validatedArgs, signal, (partialResult) => {
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
- stream.push({
244
- type: "tool_execution_end",
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
- result,
248
- isError,
303
+ args: toolCall.arguments,
249
304
  });
250
- const toolResultMessage = {
251
- role: "toolResult",
252
- toolCallId: toolCall.id,
253
- toolName: toolCall.name,
254
- content: result.content,
255
- details: result.details,
256
- isError,
257
- timestamp: Date.now(),
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
- results.push(toolResultMessage);
260
- stream.push({ type: "message_start", message: toolResultMessage });
261
- stream.push({ type: "message_end", message: toolResultMessage });
262
- // Check for steering messages - skip remaining tools if user interrupted
263
- if (getSteeringMessages) {
264
- const steering = await getSteeringMessages();
265
- if (steering.length > 0) {
266
- steeringMessages = steering;
267
- const remainingCalls = toolCalls.slice(index + 1);
268
- for (const skipped of remainingCalls) {
269
- results.push(skipToolCall(skipped, stream));
270
- }
271
- break;
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 skipToolCall(toolCall, stream) {
278
- const result = {
279
- content: [{ type: "text", text: "Skipped due to queued user message." }],
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
- stream.push({
283
- type: "tool_execution_start",
284
- toolCallId: toolCall.id,
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: true,
458
+ toolCallId: finalized.toolCall.id,
459
+ toolName: finalized.toolCall.name,
460
+ result: finalized.result,
461
+ isError: finalized.isError,
294
462
  });
295
- const toolResultMessage = {
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: true,
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
- stream.push({ type: "message_start", message: toolResultMessage });
305
- stream.push({ type: "message_end", message: toolResultMessage });
306
- return toolResultMessage;
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