@oh-my-pi/pi-agent-core 14.7.3 → 14.7.4
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 +45 -7
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": "14.7.
|
|
4
|
+
"version": "14.7.4",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
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": "14.7.
|
|
39
|
-
"@oh-my-pi/pi-natives": "14.7.
|
|
40
|
-
"@oh-my-pi/pi-utils": "14.7.
|
|
38
|
+
"@oh-my-pi/pi-ai": "14.7.4",
|
|
39
|
+
"@oh-my-pi/pi-natives": "14.7.4",
|
|
40
|
+
"@oh-my-pi/pi-utils": "14.7.4"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@sinclair/typebox": "^0.34.49",
|
package/src/agent-loop.ts
CHANGED
|
@@ -25,6 +25,46 @@ import type {
|
|
|
25
25
|
/** Sentinel returned by the abort race in `streamAssistantResponse`. */
|
|
26
26
|
const ABORTED: unique symbol = Symbol("agent-loop-aborted");
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Normalize a value coming back from `tool.execute()` (or its streaming partial-update callback)
|
|
30
|
+
* into a structurally valid {@link AgentToolResult}.
|
|
31
|
+
*
|
|
32
|
+
* The tool interface is typed, but third-party tools (MCP, extensions, user-authored AgentTools)
|
|
33
|
+
* can violate the contract at runtime. Persisting a malformed result corrupts the session file
|
|
34
|
+
* (missing `content` array → crash on reload). We coerce at the single boundary where untyped
|
|
35
|
+
* results enter the agent loop, so every downstream consumer can rely on the type.
|
|
36
|
+
*/
|
|
37
|
+
function coerceToolResult(raw: unknown): { result: AgentToolResult<any>; malformed: boolean } {
|
|
38
|
+
const rawObj = raw && typeof raw === "object" ? (raw as Record<string, unknown>) : null;
|
|
39
|
+
const rawContent = rawObj?.content;
|
|
40
|
+
const details = rawObj && "details" in rawObj ? rawObj.details : {};
|
|
41
|
+
|
|
42
|
+
if (!Array.isArray(rawContent)) {
|
|
43
|
+
return {
|
|
44
|
+
result: {
|
|
45
|
+
content: [{ type: "text", text: "Tool returned an invalid result: missing content array." }],
|
|
46
|
+
details,
|
|
47
|
+
},
|
|
48
|
+
malformed: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const content: AgentToolResult["content"] = [];
|
|
53
|
+
for (const block of rawContent) {
|
|
54
|
+
if (!block || typeof block !== "object" || !("type" in block)) continue;
|
|
55
|
+
if (block.type === "text" && typeof (block as { text?: unknown }).text === "string") {
|
|
56
|
+
content.push({ type: "text", text: sanitizeText((block as { text: string }).text) });
|
|
57
|
+
} else if (
|
|
58
|
+
block.type === "image" &&
|
|
59
|
+
typeof (block as { data?: unknown }).data === "string" &&
|
|
60
|
+
typeof (block as { mimeType?: unknown }).mimeType === "string"
|
|
61
|
+
) {
|
|
62
|
+
content.push(block as { type: "image"; data: string; mimeType: string });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { result: { content, details }, malformed: false };
|
|
66
|
+
}
|
|
67
|
+
|
|
28
68
|
/**
|
|
29
69
|
* Start an agent loop with a new prompt message.
|
|
30
70
|
* The prompt is added to the context and events are emitted for it.
|
|
@@ -656,7 +696,7 @@ async function executeToolCalls(
|
|
|
656
696
|
toolCalls: toolCallInfos,
|
|
657
697
|
})
|
|
658
698
|
: undefined;
|
|
659
|
-
|
|
699
|
+
const rawResult = await tool.execute(
|
|
660
700
|
toolCall.id,
|
|
661
701
|
transformToolCallArguments ? transformToolCallArguments(effectiveArgs, toolCall.name) : effectiveArgs,
|
|
662
702
|
tool.nonAbortable ? undefined : toolSignal,
|
|
@@ -666,16 +706,14 @@ async function executeToolCalls(
|
|
|
666
706
|
toolCallId: toolCall.id,
|
|
667
707
|
toolName: toolCall.name,
|
|
668
708
|
args: argsForExecution,
|
|
669
|
-
partialResult:
|
|
670
|
-
...partialResult,
|
|
671
|
-
content: partialResult.content.map(c =>
|
|
672
|
-
c.type === "text" ? { ...c, text: sanitizeText(c.text) } : c,
|
|
673
|
-
),
|
|
674
|
-
},
|
|
709
|
+
partialResult: coerceToolResult(partialResult).result,
|
|
675
710
|
});
|
|
676
711
|
},
|
|
677
712
|
toolContext,
|
|
678
713
|
);
|
|
714
|
+
const coerced = coerceToolResult(rawResult);
|
|
715
|
+
result = coerced.result;
|
|
716
|
+
if (coerced.malformed) isError = true;
|
|
679
717
|
} catch (e) {
|
|
680
718
|
result = {
|
|
681
719
|
content: [{ type: "text", text: e instanceof Error ? e.message : String(e) }],
|