@oh-my-pi/pi-agent-core 15.5.11 → 15.5.12
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/package.json +4 -4
- package/src/agent-loop.ts +36 -5
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-agent-core",
|
|
4
|
-
"version": "15.5.
|
|
4
|
+
"version": "15.5.12",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"fmt": "biome format --write ."
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@oh-my-pi/pi-ai": "15.5.
|
|
39
|
-
"@oh-my-pi/pi-natives": "15.5.
|
|
40
|
-
"@oh-my-pi/pi-utils": "15.5.
|
|
38
|
+
"@oh-my-pi/pi-ai": "15.5.12",
|
|
39
|
+
"@oh-my-pi/pi-natives": "15.5.12",
|
|
40
|
+
"@oh-my-pi/pi-utils": "15.5.12",
|
|
41
41
|
"@opentelemetry/api": "^1.9.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -562,9 +562,19 @@ async function runLoopBody(
|
|
|
562
562
|
return;
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
-
//
|
|
566
|
-
|
|
567
|
-
|
|
565
|
+
// Tool execution is gated on the model's *stop reason* (`toolUse`), not the
|
|
566
|
+
// mere presence of toolCall blocks. Anthropic's documented agentic loop runs
|
|
567
|
+
// tools "while stop_reason == tool_use" and exits on any other reason. With
|
|
568
|
+
// adaptive/interleaved thinking a turn can emit tool calls and then end
|
|
569
|
+
// naturally (`end_turn` → `stop`) when the model decides to wrap up — those
|
|
570
|
+
// calls are abandoned. Executing them and appending tool_results yields an
|
|
571
|
+
// invalid continuation (Anthropic rejects continuing an ended turn), which is
|
|
572
|
+
// what broke interleaved tool use. Providers set `toolUse` whenever they
|
|
573
|
+
// genuinely want tools run (Anthropic on `tool_use`; OpenAI-style providers
|
|
574
|
+
// promote `stop`→`toolUse` whenever tool-call blocks are emitted).
|
|
575
|
+
type ToolCallContent = Extract<AssistantMessage["content"][number], { type: "toolCall" }>;
|
|
576
|
+
const toolCalls = message.content.filter((c): c is ToolCallContent => c.type === "toolCall");
|
|
577
|
+
hasMoreToolCalls = message.stopReason === "toolUse" && toolCalls.length > 0;
|
|
568
578
|
|
|
569
579
|
const toolResults: ToolResultMessage[] = [];
|
|
570
580
|
if (hasMoreToolCalls) {
|
|
@@ -585,6 +595,22 @@ async function runLoopBody(
|
|
|
585
595
|
currentContext.messages.push(result);
|
|
586
596
|
newMessages.push(result);
|
|
587
597
|
}
|
|
598
|
+
} else if (toolCalls.length > 0) {
|
|
599
|
+
// Model ended the turn (stopReason !== "toolUse") but left toolCall blocks
|
|
600
|
+
// behind. They were abandoned, so don't execute or continue — but pair each
|
|
601
|
+
// with a placeholder result to keep the tool_use/tool_result contract valid
|
|
602
|
+
// for any later request that replays this turn.
|
|
603
|
+
for (const toolCall of toolCalls) {
|
|
604
|
+
const result = createAbortedToolResult(toolCall, stream, "skipped");
|
|
605
|
+
currentContext.messages.push(result);
|
|
606
|
+
newMessages.push(result);
|
|
607
|
+
toolResults.push(result);
|
|
608
|
+
recordSkippedTool(telemetry, {
|
|
609
|
+
toolCallId: toolCall.id,
|
|
610
|
+
toolName: toolCall.name,
|
|
611
|
+
status: "skipped",
|
|
612
|
+
});
|
|
613
|
+
}
|
|
588
614
|
}
|
|
589
615
|
|
|
590
616
|
stream.push({ type: "turn_end", message, toolResults });
|
|
@@ -1241,10 +1267,15 @@ async function executeToolCalls(
|
|
|
1241
1267
|
function createAbortedToolResult(
|
|
1242
1268
|
toolCall: Extract<AssistantMessage["content"][number], { type: "toolCall" }>,
|
|
1243
1269
|
stream: EventStream<AgentEvent, AgentMessage[]>,
|
|
1244
|
-
reason: "aborted" | "error",
|
|
1270
|
+
reason: "aborted" | "error" | "skipped",
|
|
1245
1271
|
errorMessage?: string,
|
|
1246
1272
|
): ToolResultMessage {
|
|
1247
|
-
const message =
|
|
1273
|
+
const message =
|
|
1274
|
+
reason === "aborted"
|
|
1275
|
+
? "Tool execution was aborted"
|
|
1276
|
+
: reason === "skipped"
|
|
1277
|
+
? "Tool call was not executed because the assistant ended its turn"
|
|
1278
|
+
: "Tool execution failed due to an error";
|
|
1248
1279
|
const result: AgentToolResult<any> = {
|
|
1249
1280
|
content: [{ type: "text", text: errorMessage ? `${message}: ${errorMessage}` : `${message}.` }],
|
|
1250
1281
|
details: {},
|