@oh-my-pi/pi-agent-core 15.8.0 → 15.8.3
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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.8.3] - 2026-06-03
|
|
6
|
+
### Added
|
|
7
|
+
|
|
8
|
+
- Added `getReadToolPath(context)` to `@oh-my-pi/pi-agent-core/compaction/tool-protection` to extract a paired `read` tool call's `path` for embedders building read-targeted protection matchers
|
|
9
|
+
- Added `getReadToolPath(context)` to `@oh-my-pi/pi-agent-core/compaction/tool-protection`: the shared primitive that extracts a paired `read` tool call's `path` argument, so embedders can build their own read-targeted compaction protection matchers (e.g. plan-file reads) the same way `isSkillReadToolResult` does.
|
|
10
|
+
|
|
11
|
+
## [15.8.2] - 2026-06-03
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added optional `AgentTool.matcherDigest(args)` hook: tools whose streamed arguments encode content in a wire grammar (patch formats, escaped strings) can expose the real content they introduce, so stream-content matchers (e.g. TTSR rules) run against plain source text instead of the wire format.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Fixed the agent loop wedging the model when a `write`/`edit` tool call is truncated by `stop_reason: length` (e.g. an OpenCode Zen / Claude-3.5-Haiku turn that emits >~1000 lines of code, blowing past the 8K `max_tokens` output cap). The skipped tool result now surfaces an actionable hint — naming `stop_reason: length` and telling the model to split the payload into multiple smaller calls — instead of the generic "Tool call was not executed because the assistant ended its turn" placeholder, which left the auto-continue loop re-emitting the same oversized payload until the user gave up. Tools are still NOT executed when the arguments are truncated. ([#1785](https://github.com/can1357/oh-my-pi/issues/1785))
|
|
20
|
+
|
|
5
21
|
## [15.8.0] - 2026-06-02
|
|
6
22
|
|
|
7
23
|
### Fixed
|
|
@@ -551,4 +567,4 @@ Initial release under @oh-my-pi scope. See previous releases at [badlogic/pi-mon
|
|
|
551
567
|
|
|
552
568
|
- `Agent` constructor now has all options optional (empty options use defaults).
|
|
553
569
|
|
|
554
|
-
- `queueMessage()` is now synchronous (no longer returns a Promise).
|
|
570
|
+
- `queueMessage()` is now synchronous (no longer returns a Promise).
|
|
@@ -7,5 +7,11 @@ export interface ProtectedToolContext {
|
|
|
7
7
|
}
|
|
8
8
|
export type ProtectedToolMatcher = string | ((context: ProtectedToolContext) => boolean);
|
|
9
9
|
export declare function collectToolCallsById(entries: readonly SessionEntry[]): Map<string, AgentToolCall>;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Extract the `path` argument from a paired `read` tool call, when the result
|
|
12
|
+
* is a `read` result carrying a string path. Returns `undefined` otherwise.
|
|
13
|
+
* Shared primitive for read-targeted protection matchers (skills, plans, …).
|
|
14
|
+
*/
|
|
15
|
+
export declare function getReadToolPath({ toolResult, toolCall }: ProtectedToolContext): string | undefined;
|
|
16
|
+
export declare function isSkillReadToolResult(context: ProtectedToolContext): boolean;
|
|
11
17
|
export declare function isProtectedToolResult(toolResult: ToolResultMessage, toolCall: AgentToolCall | undefined, matchers: readonly ProtectedToolMatcher[]): boolean;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -370,6 +370,14 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
|
|
|
370
370
|
* - function: `_i` is NOT injected; intent is derived dynamically from (potentially partial / streaming) args.
|
|
371
371
|
*/
|
|
372
372
|
intent?: "omit" | "optional" | "require" | ((args: Partial<Static<TParameters>>) => string | undefined);
|
|
373
|
+
/**
|
|
374
|
+
* Normalize (potentially partial) streamed arguments into the plain text that
|
|
375
|
+
* stream-content matchers (e.g. TTSR rules) should inspect — the real content
|
|
376
|
+
* the call introduces, without wire grammar such as patch prefixes or JSON
|
|
377
|
+
* string escaping. Return `undefined` to fall back to raw argument-delta
|
|
378
|
+
* matching.
|
|
379
|
+
*/
|
|
380
|
+
matcherDigest?: (args: unknown) => string | undefined;
|
|
373
381
|
/** Capability tier declaration used by approval gates. Omitted means "exec". */
|
|
374
382
|
approval?: ToolApproval;
|
|
375
383
|
/** Lines appended after the standard approval prompt header. */
|
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.8.
|
|
4
|
+
"version": "15.8.3",
|
|
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.8.
|
|
39
|
-
"@oh-my-pi/pi-natives": "15.8.
|
|
40
|
-
"@oh-my-pi/pi-utils": "15.8.
|
|
38
|
+
"@oh-my-pi/pi-ai": "15.8.3",
|
|
39
|
+
"@oh-my-pi/pi-natives": "15.8.3",
|
|
40
|
+
"@oh-my-pi/pi-utils": "15.8.3",
|
|
41
41
|
"@opentelemetry/api": "^1.9.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/src/agent-loop.ts
CHANGED
|
@@ -627,9 +627,12 @@ async function runLoopBody(
|
|
|
627
627
|
// toolCall blocks behind. The trailing call's arguments may be incomplete,
|
|
628
628
|
// so don't execute or continue — pair each with a placeholder result to keep
|
|
629
629
|
// the tool_use/tool_result contract valid for any later request that
|
|
630
|
-
// replays this turn.
|
|
630
|
+
// replays this turn. When the truncation was `length`, surface an actionable
|
|
631
|
+
// hint so the model doesn't loop by re-emitting the same oversized payload
|
|
632
|
+
// (e.g. 1000+ line `write` content blowing past the model's output cap).
|
|
633
|
+
const skipReason = message.stopReason === "length" ? "length" : "skipped";
|
|
631
634
|
for (const toolCall of toolCalls) {
|
|
632
|
-
const result = createAbortedToolResult(toolCall, stream,
|
|
635
|
+
const result = createAbortedToolResult(toolCall, stream, skipReason);
|
|
633
636
|
currentContext.messages.push(result);
|
|
634
637
|
newMessages.push(result);
|
|
635
638
|
toolResults.push(result);
|
|
@@ -1360,15 +1363,17 @@ async function executeToolCalls(
|
|
|
1360
1363
|
function createAbortedToolResult(
|
|
1361
1364
|
toolCall: Extract<AssistantMessage["content"][number], { type: "toolCall" }>,
|
|
1362
1365
|
stream: EventStream<AgentEvent, AgentMessage[]>,
|
|
1363
|
-
reason: "aborted" | "error" | "skipped",
|
|
1366
|
+
reason: "aborted" | "error" | "skipped" | "length",
|
|
1364
1367
|
errorMessage?: string,
|
|
1365
1368
|
): ToolResultMessage {
|
|
1366
1369
|
const message =
|
|
1367
1370
|
reason === "aborted"
|
|
1368
1371
|
? "Tool execution was aborted"
|
|
1369
|
-
: reason === "
|
|
1370
|
-
? "Tool call was not executed because the assistant
|
|
1371
|
-
:
|
|
1372
|
+
: reason === "length"
|
|
1373
|
+
? "Tool call was not executed because the assistant hit its output token limit (stop_reason: length) before the arguments could complete; the recorded arguments are truncated and unsafe to run. Do NOT retry by re-emitting the same large payload — split the work into several smaller tool calls (e.g. for `write`/`edit`, write the first chunk then append the rest with subsequent `edit` insert ops, or break the file into multiple `write` targets)"
|
|
1374
|
+
: reason === "skipped"
|
|
1375
|
+
? "Tool call was not executed because the assistant ended its turn"
|
|
1376
|
+
: "Tool execution failed due to an error";
|
|
1372
1377
|
const result: AgentToolResult<any> = {
|
|
1373
1378
|
content: [{ type: "text", text: errorMessage ? `${message}: ${errorMessage}` : `${message}.` }],
|
|
1374
1379
|
details: {},
|
|
@@ -24,10 +24,19 @@ export function collectToolCallsById(entries: readonly SessionEntry[]): Map<stri
|
|
|
24
24
|
return toolCalls;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Extract the `path` argument from a paired `read` tool call, when the result
|
|
29
|
+
* is a `read` result carrying a string path. Returns `undefined` otherwise.
|
|
30
|
+
* Shared primitive for read-targeted protection matchers (skills, plans, …).
|
|
31
|
+
*/
|
|
32
|
+
export function getReadToolPath({ toolResult, toolCall }: ProtectedToolContext): string | undefined {
|
|
33
|
+
if (toolResult.toolName !== "read" || toolCall?.name !== "read") return undefined;
|
|
29
34
|
const path = (toolCall.arguments as Record<string, unknown>).path;
|
|
30
|
-
return typeof path === "string"
|
|
35
|
+
return typeof path === "string" ? path : undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isSkillReadToolResult(context: ProtectedToolContext): boolean {
|
|
39
|
+
return getReadToolPath(context)?.startsWith(SKILL_INTERNAL_URL_PREFIX) ?? false;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export function isProtectedToolResult(
|
package/src/types.ts
CHANGED
|
@@ -441,6 +441,15 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
|
|
|
441
441
|
*/
|
|
442
442
|
intent?: "omit" | "optional" | "require" | ((args: Partial<Static<TParameters>>) => string | undefined);
|
|
443
443
|
|
|
444
|
+
/**
|
|
445
|
+
* Normalize (potentially partial) streamed arguments into the plain text that
|
|
446
|
+
* stream-content matchers (e.g. TTSR rules) should inspect — the real content
|
|
447
|
+
* the call introduces, without wire grammar such as patch prefixes or JSON
|
|
448
|
+
* string escaping. Return `undefined` to fall back to raw argument-delta
|
|
449
|
+
* matching.
|
|
450
|
+
*/
|
|
451
|
+
matcherDigest?: (args: unknown) => string | undefined;
|
|
452
|
+
|
|
444
453
|
/** Capability tier declaration used by approval gates. Omitted means "exec". */
|
|
445
454
|
approval?: ToolApproval;
|
|
446
455
|
|