@linxiraos/pi-agent-core 1.3.0 → 1.3.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.
- package/CHANGELOG.md +41 -31
- package/package.json +8 -8
- package/src/prompts/dsml-tool-call-leak.md +1 -1
- package/src/tokenizer.ts +18 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,42 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
-
## [1.3.
|
|
4
|
-
|
|
5
|
-
### Breaking Changes
|
|
6
|
-
|
|
7
|
-
- Removed `estimateImageContentTokens()` from `@linxiraos/pi-agent-core/image-tokens`; use `Tokenizer.imageTokens(size, detail)`, which prices with the reading model's catalog image rule ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
8
|
-
- `applyShakeRegion()` and `applyShakeRegions()` take the replacement's token count (`replacementTokens`); a block rewrite of an assistant turn adds the tokens it removes to `elidedOutputTokens`, so a replayed Claude turn's count stays lowered after a reload ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
9
|
-
- Removed `ApprovalDetailsOptions`; `formatApprovalDetails` takes only `args` again, since the compact approval prompt no longer shows tool details ([#15642](https://github.com/can1357/oh-my-pi/pull/15642) by [@H4vC](https://github.com/H4vC))
|
|
10
|
-
|
|
11
|
-
### Added
|
|
12
|
-
|
|
13
|
-
- Added `AgentLoopConfig.onDetachedToolUpdate`: a tool's `onUpdate` frames that arrive after its run's event stream ended (a returned call whose background job keeps reporting) reach it instead of being dropped; `Agent` forwards them to its subscribers as `tool_execution_update` events
|
|
14
|
-
- `AgentTool.formatApprovalDetails` receives `ApprovalDetailsOptions`; `compact: true` asks for untruncated bodies so a compact approval prompt can count their lines ([#15550](https://github.com/can1357/oh-my-pi/pull/15550) by [@H4vC](https://github.com/H4vC))
|
|
15
|
-
- `Tokenizer.countLatestTurn`: counts a message as the transcript's latest assistant turn, which an Anthropic Messages replay keeps byte-for-byte ([#15791](https://github.com/can1357/oh-my-pi/pull/15791) by [@will-bogusz](https://github.com/will-bogusz))
|
|
16
|
-
- `Agent.prioritizeSteering`: moves given pending steering messages ahead of older ones so one-at-a-time delivery takes them first ([#15642](https://github.com/can1357/oh-my-pi/pull/15642))
|
|
17
|
-
- `AgentTool.delegationGuidance`: instructions a same-name extension replacement inherits in its description when it delegates to the native tool ([#15477](https://github.com/can1357/oh-my-pi/pull/15477) by [@iliaal](https://github.com/iliaal))
|
|
18
|
-
- `CompactionPreparation.lastSummarizedEntryId` and `CompactionResult.providerReplayThroughEntryId`: a remote compaction that could only fit the history before the kept tail reports the last entry its native history covers; later entries replay normally ([#15712](https://github.com/can1357/oh-my-pi/pull/15712) by [@H4vC](https://github.com/H4vC))
|
|
19
|
-
- `agent_start` carries `resumedToolCallIds` when a continued run first executes the calls of an unpaired assistant tail
|
|
20
|
-
- `prepareQueuedMessages` receives which queue (`steering` or `followUp`) a batch came from ([#16026](https://github.com/can1357/oh-my-pi/pull/16026))
|
|
3
|
+
## [1.3.1] - 2026-10-19
|
|
21
4
|
|
|
22
5
|
### Changed
|
|
23
6
|
|
|
24
|
-
-
|
|
7
|
+
- The failed-tool-call correction sent to DeepSeek models now also covers chat-template tool-call tokens written out as text, not only malformed DSML ([#16216](https://github.com/can1357/oh-my-pi/pull/16216) by [@H4vC](https://github.com/H4vC))
|
|
25
8
|
|
|
26
9
|
### Fixed
|
|
27
10
|
|
|
28
|
-
- Fixed
|
|
29
|
-
- Fixed screenshots and other non-frame images being counted at the OpenAI rate for every model instead of the reading model's own; Claude billed them about 16% higher ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
30
|
-
- Fixed OpenAI Responses and Codex context estimates leaving out replayed reasoning: each turn whose encrypted reasoning actually replays now counts its reported reasoning tokens (its ciphertext when the backend reports none), and none while a cold session rebuilds it out of the request, so compaction and snapcompact sizing match billing; branch summaries no longer budget for encrypted reasoning they never send ([#15447](https://github.com/can1357/oh-my-pi/pull/15447) by [@will-bogusz](https://github.com/will-bogusz))
|
|
31
|
-
- Fixed a Claude compaction or branch summarizer receiving assistant reasoning from a snapcompact archive rendered under another model, which Claude refuses (`reasoning_extraction`) ([#15436](https://github.com/can1357/oh-my-pi/pull/15436) by [@will-bogusz](https://github.com/will-bogusz))
|
|
32
|
-
- Fixed a Cursor tool call re-sent by the server persisting two results under one id; the re-run's result replaces the first ([#15749](https://github.com/can1357/oh-my-pi/issues/15749))
|
|
33
|
-
- Fixed compaction keeping more recent history than `keepRecentTokens` when the latest Claude turn on the Anthropic Messages API ended its tool calls on `stop` or `length`: the replay keeps that turn's signed reasoning, but the cut point counted only its visible blocks ([#15791](https://github.com/can1357/oh-my-pi/pull/15791) by [@will-bogusz](https://github.com/will-bogusz))
|
|
34
|
-
- Fixed native (OpenAI/Codex) remote compaction refusing or truncating sessions that fit the window: its preflight counted the JSON-escaped request, inflating quote-, backslash- and newline-heavy tool output such as disassembly by ~10–20%; it now counts text as the model reads it ([#15712](https://github.com/can1357/oh-my-pi/pull/15712) by [@H4vC](https://github.com/H4vC))
|
|
35
|
-
- Fixed OpenAI V1/V2 remote compaction failing outright when the history, kept tail included, exceeds the context window (a large final response or tool output after the last check); it retries on the history before the kept tail ([#15712](https://github.com/can1357/oh-my-pi/pull/15712) by [@H4vC](https://github.com/H4vC))
|
|
36
|
-
- Fixed fitted output caps overflowing the context window on strict hosts (llama.cpp-family) after turns with many parallel tool results; chat-template framing is now charged per locally counted message ([#15699](https://github.com/can1357/oh-my-pi/issues/15699))
|
|
37
|
-
- Fixed leaked DeepSeek DSML markup reaching saved history from reasoning blocks and from turns that also made a valid tool call ([#15485](https://github.com/can1357/oh-my-pi/issues/15485))
|
|
38
|
-
- Cancelling an `ask` after a background job finishes now reports the user cancellation instead of telling the model to retry a skipped tool ([#15985](https://github.com/can1357/oh-my-pi/issues/15985))
|
|
39
|
-
- Fixed Cursor turns failing with `PROVIDER_ERROR` when a follow-up (a finished background job, a late diagnostic, a reminder) arrived while the previous turn was still streaming ([#15821](https://github.com/can1357/oh-my-pi/pull/15821) by [@robfeldmann](https://github.com/robfeldmann))
|
|
11
|
+
- Fixed Ollama chat history estimates counting stripped native thinking and missing foreign thinking replayed as text, which skewed compaction after model switches ([#16164](https://github.com/can1357/oh-my-pi/issues/16164))
|
|
40
12
|
|
|
41
13
|
## [9.2.2] - 2026-01-31
|
|
42
14
|
|
|
@@ -168,6 +140,44 @@
|
|
|
168
140
|
|
|
169
141
|
Initial release under @oh-my-pi scope. See previous releases at [badlogic/pi-mono](https://github.com/badlogic/pi-mono).
|
|
170
142
|
|
|
143
|
+
## [1.3.0] - 2026-10-18
|
|
144
|
+
|
|
145
|
+
### Fixed
|
|
146
|
+
|
|
147
|
+
- Fixed Cursor turns failing with `PROVIDER_ERROR` when a follow-up (a finished background job, a late diagnostic, a reminder) arrived while the previous turn was still streaming ([#15821](https://github.com/can1357/oh-my-pi/pull/15821) by [@robfeldmann](https://github.com/robfeldmann))
|
|
148
|
+
- Cancelling an `ask` after a background job finishes now reports the user cancellation instead of telling the model to retry a skipped tool ([#15985](https://github.com/can1357/oh-my-pi/issues/15985))
|
|
149
|
+
- Fixed a Cursor tool call re-sent by the server persisting two results under one id; the re-run's result replaces the first ([#15749](https://github.com/can1357/oh-my-pi/issues/15749))
|
|
150
|
+
- Fixed compaction keeping more recent history than `keepRecentTokens` when the latest Claude turn on the Anthropic Messages API ended its tool calls on `stop` or `length`: the replay keeps that turn's signed reasoning, but the cut point counted only its visible blocks ([#15791](https://github.com/can1357/oh-my-pi/pull/15791) by [@will-bogusz](https://github.com/will-bogusz))
|
|
151
|
+
- Fixed Claude context counts reading Opus 5.5 requests about 55% over their billed input: a replayed thinking turn now counts at the output size Claude billed for it, not its signature's bytes ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
152
|
+
- Fixed screenshots and other non-frame images being counted at the OpenAI rate for every model instead of the reading model's own; Claude billed them about 16% higher ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
153
|
+
- Fixed OpenAI Responses and Codex context estimates leaving out replayed reasoning: each turn whose encrypted reasoning actually replays now counts its reported reasoning tokens (its ciphertext when the backend reports none), and none while a cold session rebuilds it out of the request, so compaction and snapcompact sizing match billing; branch summaries no longer budget for encrypted reasoning they never send ([#15447](https://github.com/can1357/oh-my-pi/pull/15447) by [@will-bogusz](https://github.com/will-bogusz))
|
|
154
|
+
- Fixed a Claude compaction or branch summarizer receiving assistant reasoning from a snapcompact archive rendered under another model, which Claude refuses (`reasoning_extraction`) ([#15436](https://github.com/can1357/oh-my-pi/pull/15436) by [@will-bogusz](https://github.com/will-bogusz))
|
|
155
|
+
- Fixed native (OpenAI/Codex) remote compaction refusing or truncating sessions that fit the window: its preflight counted the JSON-escaped request, inflating quote-, backslash- and newline-heavy tool output such as disassembly by ~10–20%; it now counts text as the model reads it ([#15712](https://github.com/can1357/oh-my-pi/pull/15712) by [@H4vC](https://github.com/H4vC))
|
|
156
|
+
- Fixed OpenAI V1/V2 remote compaction failing outright when the history, kept tail included, exceeds the context window (a large final response or tool output after the last check); it retries on the history before the kept tail ([#15712](https://github.com/can1357/oh-my-pi/pull/15712) by [@H4vC](https://github.com/H4vC))
|
|
157
|
+
- Fixed fitted output caps overflowing the context window on strict hosts (llama.cpp-family) after turns with many parallel tool results; chat-template framing is now charged per locally counted message ([#15699](https://github.com/can1357/oh-my-pi/issues/15699))
|
|
158
|
+
- Fixed leaked DeepSeek DSML markup reaching saved history from reasoning blocks and from turns that also made a valid tool call ([#15485](https://github.com/can1357/oh-my-pi/issues/15485))
|
|
159
|
+
|
|
160
|
+
### Added
|
|
161
|
+
|
|
162
|
+
- `prepareQueuedMessages` receives which queue (`steering` or `followUp`) a batch came from ([#16026](https://github.com/can1357/oh-my-pi/pull/16026))
|
|
163
|
+
- `agent_start` carries `resumedToolCallIds` when a continued run first executes the calls of an unpaired assistant tail
|
|
164
|
+
- `Tokenizer.countLatestTurn`: counts a message as the transcript's latest assistant turn, which an Anthropic Messages replay keeps byte-for-byte ([#15791](https://github.com/can1357/oh-my-pi/pull/15791) by [@will-bogusz](https://github.com/will-bogusz))
|
|
165
|
+
- `Agent.prioritizeSteering`: moves given pending steering messages ahead of older ones so one-at-a-time delivery takes them first ([#15642](https://github.com/can1357/oh-my-pi/pull/15642))
|
|
166
|
+
- Added `AgentLoopConfig.onDetachedToolUpdate`: a tool's `onUpdate` frames that arrive after its run's event stream ended (a returned call whose background job keeps reporting) reach it instead of being dropped; `Agent` forwards them to its subscribers as `tool_execution_update` events
|
|
167
|
+
- `AgentTool.formatApprovalDetails` receives `ApprovalDetailsOptions`; `compact: true` asks for untruncated bodies so a compact approval prompt can count their lines ([#15550](https://github.com/can1357/oh-my-pi/pull/15550) by [@H4vC](https://github.com/H4vC))
|
|
168
|
+
- `AgentTool.delegationGuidance`: instructions a same-name extension replacement inherits in its description when it delegates to the native tool ([#15477](https://github.com/can1357/oh-my-pi/pull/15477) by [@iliaal](https://github.com/iliaal))
|
|
169
|
+
- `CompactionPreparation.lastSummarizedEntryId` and `CompactionResult.providerReplayThroughEntryId`: a remote compaction that could only fit the history before the kept tail reports the last entry its native history covers; later entries replay normally ([#15712](https://github.com/can1357/oh-my-pi/pull/15712) by [@H4vC](https://github.com/H4vC))
|
|
170
|
+
|
|
171
|
+
### Breaking Changes
|
|
172
|
+
|
|
173
|
+
- Removed `estimateImageContentTokens()` from `@linxiraos/pi-agent-core/image-tokens`; use `Tokenizer.imageTokens(size, detail)`, which prices with the reading model's catalog image rule ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
174
|
+
- `applyShakeRegion()` and `applyShakeRegions()` take the replacement's token count (`replacementTokens`); a block rewrite of an assistant turn adds the tokens it removes to `elidedOutputTokens`, so a replayed Claude turn's count stays lowered after a reload ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
175
|
+
- Removed `ApprovalDetailsOptions`; `formatApprovalDetails` takes only `args` again, since the compact approval prompt no longer shows tool details ([#15642](https://github.com/can1357/oh-my-pi/pull/15642) by [@H4vC](https://github.com/H4vC))
|
|
176
|
+
|
|
177
|
+
### Changed
|
|
178
|
+
|
|
179
|
+
- Before-model-call hooks now receive the loop context as a second argument, and `buildProviderContext` receives the summarizer model ([#15436](https://github.com/can1357/oh-my-pi/pull/15436) by [@will-bogusz](https://github.com/will-bogusz))
|
|
180
|
+
|
|
171
181
|
## [1.2.0] - 2026-10-12
|
|
172
182
|
|
|
173
183
|
- 版本线推进至 1.2.0;本版无独立用户可见变化。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@linxiraos/pi-agent-core",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.1",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://linxira-os.github.io/zeta/",
|
|
7
7
|
"author": "Linxira-OS",
|
|
@@ -35,16 +35,16 @@
|
|
|
35
35
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@linxiraos/pi-ai": "1.3.
|
|
39
|
-
"@linxiraos/pi-catalog": "1.3.
|
|
40
|
-
"@linxiraos/pi-natives": "1.3.
|
|
41
|
-
"@linxiraos/pi-utils": "1.3.
|
|
42
|
-
"@linxiraos/pi-wire": "1.3.
|
|
43
|
-
"@linxiraos/pi-snapcompact": "1.3.
|
|
38
|
+
"@linxiraos/pi-ai": "1.3.1",
|
|
39
|
+
"@linxiraos/pi-catalog": "1.3.1",
|
|
40
|
+
"@linxiraos/pi-natives": "1.3.1",
|
|
41
|
+
"@linxiraos/pi-utils": "1.3.1",
|
|
42
|
+
"@linxiraos/pi-wire": "1.3.1",
|
|
43
|
+
"@linxiraos/pi-snapcompact": "1.3.1",
|
|
44
44
|
"@opentelemetry/api": "^1.9.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@linxiraos/pi-omptype": "1.3.
|
|
47
|
+
"@linxiraos/pi-omptype": "1.3.1",
|
|
48
48
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
49
49
|
"@opentelemetry/sdk-trace-base": "^2.9.0",
|
|
50
50
|
"@types/bun": "1.4.3"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Tool call failed: your previous response tried to invoke a tool, but the
|
|
1
|
+
Tool call failed: your previous response tried to invoke a tool, but the tool-call markup was malformed, so it arrived as plain text, no tool was executed, and the broken markup was removed from your response. Usually the opening `<|DSML|tool_calls>` and/or `<|DSML|invoke name="…">` tags are missing, the tool name was written as a plain line, or the chat template's tool-call special tokens were typed out as text.
|
|
2
2
|
|
|
3
3
|
A tool call must be one complete envelope:
|
|
4
4
|
|
package/src/tokenizer.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { AssistantMessage, Model } from "@linxiraos/pi-ai";
|
|
2
|
+
import { renderDemotedThinking } from "@linxiraos/pi-ai/dialect/demotion";
|
|
3
|
+
import { ollamaChatDropsOwnThinking } from "@linxiraos/pi-ai/providers/ollama";
|
|
2
4
|
import { isColdReplayableResponsesTurn } from "@linxiraos/pi-ai/providers/openai-shared";
|
|
3
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
isSameModelTurn,
|
|
7
|
+
shouldDropTruncatedThinkingOnlyAssistant,
|
|
8
|
+
} from "@linxiraos/pi-ai/providers/transform-messages";
|
|
4
9
|
import {
|
|
5
10
|
getOpenAIResponsesHistoryItems,
|
|
6
11
|
sanitizeOpenAIResponsesAssistantHistoryItemsForReplay,
|
|
@@ -207,6 +212,7 @@ export class Tokenizer {
|
|
|
207
212
|
readonly #frameBilling: snapcompact.FrameBilling;
|
|
208
213
|
readonly frameBillingKey: string;
|
|
209
214
|
readonly #reasoningReader: ReasoningReplayReader | undefined;
|
|
215
|
+
readonly #dropsOllamaThinking: boolean;
|
|
210
216
|
/** Claude's own API and prefix-bound hosts keep another model's signature from their deployment. */
|
|
211
217
|
readonly #readsDeploymentSignatures: boolean;
|
|
212
218
|
readonly reasoningReplayKey: string;
|
|
@@ -244,6 +250,7 @@ export class Tokenizer {
|
|
|
244
250
|
this.#frameBilling = snapcompact.frameBilling(model ?? undefined);
|
|
245
251
|
this.frameBillingKey = snapcompact.frameBillingKey(this.#frameBilling);
|
|
246
252
|
this.#reasoningReader = reasoningReplayReader(model);
|
|
253
|
+
this.#dropsOllamaThinking = ollamaChatDropsOwnThinking(model);
|
|
247
254
|
const compat = model?.compat;
|
|
248
255
|
this.#readsDeploymentSignatures =
|
|
249
256
|
model?.api === "anthropic-messages" &&
|
|
@@ -415,6 +422,8 @@ export class Tokenizer {
|
|
|
415
422
|
// reaches the wire then (the payload items replay instead); a turn whose
|
|
416
423
|
// backend reported no reasoning tokens keeps the signature as its price.
|
|
417
424
|
const replayedReasoning = this.#replayedReasoningTokens(message);
|
|
425
|
+
const ollamaReader = this.#reasoningReader?.api === "ollama-chat" ? this.#reasoningReader : undefined;
|
|
426
|
+
const ownOllamaTurn = ollamaReader !== undefined && isSameModelTurn(message, ollamaReader);
|
|
418
427
|
if (typeof replayedReasoning === "number") extra += replayedReasoning;
|
|
419
428
|
const signaturesPriced = this.thinkingReplayTokens === "signature";
|
|
420
429
|
// The replay's signature rule (`transformMessages`): an aborted or
|
|
@@ -441,6 +450,13 @@ export class Tokenizer {
|
|
|
441
450
|
if (block.type === "text") {
|
|
442
451
|
fragments.push(block.text);
|
|
443
452
|
} else if (block.type === "thinking") {
|
|
453
|
+
// `transformMessages` drops another model's empty thinking and demotes the
|
|
454
|
+
// rest to visible text; only the reader's own thinking stays native.
|
|
455
|
+
if (ollamaReader && !ownOllamaTurn) {
|
|
456
|
+
if (block.thinking.trim()) fragments.push(renderDemotedThinking(ollamaReader.id, block.thinking));
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
if (this.#dropsOllamaThinking && ownOllamaTurn) continue;
|
|
444
460
|
fragments.push(block.thinking);
|
|
445
461
|
// Providers charge for the opaque signature/reasoning payload that
|
|
446
462
|
// rides alongside the thinking text (OpenAI Responses encrypted
|
|
@@ -561,6 +577,7 @@ export class Tokenizer {
|
|
|
561
577
|
snapcompact.frameBillingKey(snapcompact.frameBilling(model ?? undefined)) === this.frameBillingKey &&
|
|
562
578
|
reasoningReplayKey(model) === this.reasoningReplayKey &&
|
|
563
579
|
resolveThinkingReplayTokens(model ?? {}) === this.thinkingReplayTokens &&
|
|
580
|
+
ollamaChatDropsOwnThinking(model) === this.#dropsOllamaThinking &&
|
|
564
581
|
(options?.nativeHistoryReplay ?? true) === this.nativeHistoryReplay
|
|
565
582
|
);
|
|
566
583
|
}
|