@agentproto/adapter-pi 0.3.5 → 0.3.7
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/PI-RPC.md +15 -4
- package/dist/index.d.ts +6 -1
- package/dist/index.mjs +17 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/PI-RPC.md
CHANGED
|
@@ -137,11 +137,22 @@ only the final `agent_end{willRetry:false}` emits `turn-end`.
|
|
|
137
137
|
## Known gaps / assumptions
|
|
138
138
|
|
|
139
139
|
- **`usage_update.size` / `.used`** — pi's per-message `Usage` reports token
|
|
140
|
-
totals + cost, but **no context-window size**.
|
|
141
|
-
|
|
140
|
+
totals + cost, but **no context-window size**. `client.ts` resolves the
|
|
141
|
+
window once per `connect()` from `@agentproto/model-catalog`'s
|
|
142
|
+
`resolveContextWindow(opts.model)` and the mapper surfaces it as `size`;
|
|
143
|
+
when the model isn't in the catalog, `size` is sent as `0` (this
|
|
144
|
+
codebase's "unknown window" sentinel — the runtime only applies `size`
|
|
145
|
+
when `> 0`, so `contextSize` is left unset rather than defaulted to a
|
|
146
|
+
token count). `used` is `usage.totalTokens`. `tokensIn`/`tokensOut` =
|
|
142
147
|
`input`/`output`; `cost = { amount: usage.cost.total, currency: "USD" }`.
|
|
143
|
-
|
|
144
|
-
`
|
|
148
|
+
Previously this mapper sent `usage.totalTokens` as BOTH `size` and `used`,
|
|
149
|
+
which pinned `contextPct` at 100% on every turn and tripped the
|
|
150
|
+
context-continuity hard stop immediately — fixed alongside a
|
|
151
|
+
`contextSize === contextUsed` guard in the runtime's
|
|
152
|
+
`computeContextPct` (`packages/runtime/src/context-continuity.ts`). A
|
|
153
|
+
further improvement could issue `get_session_stats` (which carries
|
|
154
|
+
`contextUsage`) for a live `used` figure straight from pi, at the cost of
|
|
155
|
+
an extra round-trip.
|
|
145
156
|
- **`length` → `max_turns`** — pi's `length` stop reason is a token/context
|
|
146
157
|
cap, not a turn-count cap. `max_turns` is the closest canonical "budget hit"
|
|
147
158
|
reason; there is no exact equivalent in the `StreamEvent` taxonomy.
|
package/dist/index.d.ts
CHANGED
|
@@ -160,6 +160,11 @@ declare function mapStopReason(reason: PiStopReason | undefined): "completed" |
|
|
|
160
160
|
* Translate one pi session event into zero or more {@link StreamEvent}s,
|
|
161
161
|
* updating `state` in place. Pure aside from the state mutation.
|
|
162
162
|
*
|
|
163
|
+
* `contextWindow` is the model's real context-window size (resolved by the
|
|
164
|
+
* caller from `@agentproto/model-catalog`, once per connection — pi's own
|
|
165
|
+
* per-message `Usage` carries no window figure). `undefined` when the
|
|
166
|
+
* model isn't in the catalog; threaded straight into `usageUpdate`.
|
|
167
|
+
*
|
|
163
168
|
* `agent_end` (with `willRetry` false) is the turn terminator: it flushes a
|
|
164
169
|
* `turn-end` whose reason reflects the stop reason accumulated over the turn.
|
|
165
170
|
* An `agent_end` with `willRetry: true` (auto-retry) does NOT close the turn —
|
|
@@ -168,7 +173,7 @@ declare function mapStopReason(reason: PiStopReason | undefined): "completed" |
|
|
|
168
173
|
* RPC stdout stream (verified empirically against pi 0.80.3), so it cannot be
|
|
169
174
|
* relied on as the terminator — see PI-RPC.md.
|
|
170
175
|
*/
|
|
171
|
-
declare function mapPiEvent(event: PiSessionEvent, sessionId: string, state: PiMapperState): StreamEvent[];
|
|
176
|
+
declare function mapPiEvent(event: PiSessionEvent, sessionId: string, state: PiMapperState, contextWindow: number | undefined): StreamEvent[];
|
|
172
177
|
|
|
173
178
|
/**
|
|
174
179
|
* @agentproto/adapter-pi — AIP-45 adapter for **earendil-works/pi**
|
package/dist/index.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { tmpdir } from 'os';
|
|
|
8
8
|
import { join } from 'path';
|
|
9
9
|
import { StringDecoder } from 'string_decoder';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
|
+
import { resolveContextWindow } from '@agentproto/model-catalog/llm';
|
|
11
12
|
import process3 from 'process';
|
|
12
13
|
import { PassThrough } from 'stream';
|
|
13
14
|
|
|
@@ -18518,21 +18519,25 @@ function mapStopReason(reason) {
|
|
|
18518
18519
|
return "completed";
|
|
18519
18520
|
}
|
|
18520
18521
|
}
|
|
18521
|
-
function usageUpdate(sessionId, usage) {
|
|
18522
|
+
function usageUpdate(sessionId, usage, contextWindow) {
|
|
18522
18523
|
return {
|
|
18523
18524
|
kind: "usage_update",
|
|
18524
18525
|
sessionId,
|
|
18525
18526
|
// Pi's per-message `Usage` reports token totals but not a context-window
|
|
18526
|
-
// size
|
|
18527
|
-
//
|
|
18528
|
-
|
|
18527
|
+
// size, so the caller resolves one out-of-band (model catalog, by model
|
|
18528
|
+
// id — see client.ts `connect()`). `size: 0` is this codebase's "unknown
|
|
18529
|
+
// window" sentinel (mirrors the `agy` mapper in print-arm.ts): the
|
|
18530
|
+
// runtime's usage_update ingestion only applies `size` when it's `> 0`,
|
|
18531
|
+
// so an unresolved window leaves `contextSize` untouched rather than
|
|
18532
|
+
// being defaulted to `totalTokens` (see PI-RPC.md).
|
|
18533
|
+
size: contextWindow ?? 0,
|
|
18529
18534
|
used: usage.totalTokens,
|
|
18530
18535
|
cost: { amount: usage.cost.total, currency: "USD" },
|
|
18531
18536
|
tokensIn: usage.input,
|
|
18532
18537
|
tokensOut: usage.output
|
|
18533
18538
|
};
|
|
18534
18539
|
}
|
|
18535
|
-
function mapPiEvent(event, sessionId, state) {
|
|
18540
|
+
function mapPiEvent(event, sessionId, state, contextWindow) {
|
|
18536
18541
|
switch (event.type) {
|
|
18537
18542
|
case "message_update": {
|
|
18538
18543
|
const inner = event.assistantMessageEvent;
|
|
@@ -18586,7 +18591,7 @@ function mapPiEvent(event, sessionId, state) {
|
|
|
18586
18591
|
state.lastStopReason = message.stopReason;
|
|
18587
18592
|
}
|
|
18588
18593
|
if (message?.usage !== void 0) {
|
|
18589
|
-
return [usageUpdate(sessionId, message.usage)];
|
|
18594
|
+
return [usageUpdate(sessionId, message.usage, contextWindow)];
|
|
18590
18595
|
}
|
|
18591
18596
|
return [];
|
|
18592
18597
|
}
|
|
@@ -18671,10 +18676,14 @@ function extractPromptText(message) {
|
|
|
18671
18676
|
}
|
|
18672
18677
|
return JSON.stringify(message);
|
|
18673
18678
|
}
|
|
18679
|
+
function resolvePiContextWindow(modelId) {
|
|
18680
|
+
return modelId ? resolveContextWindow(modelId)?.contextWindow : void 0;
|
|
18681
|
+
}
|
|
18674
18682
|
function createAgentCliClient(definition) {
|
|
18675
18683
|
let child;
|
|
18676
18684
|
let piSessionId;
|
|
18677
18685
|
let connectEffort;
|
|
18686
|
+
let contextWindow;
|
|
18678
18687
|
let onActivity;
|
|
18679
18688
|
let bridgeTempDir;
|
|
18680
18689
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -18735,7 +18744,7 @@ function createAgentCliClient(definition) {
|
|
|
18735
18744
|
onActivity?.();
|
|
18736
18745
|
const turn = currentTurn;
|
|
18737
18746
|
if (!turn) return;
|
|
18738
|
-
for (const mapped of mapPiEvent(event, piSessionId ?? "", mapperState)) {
|
|
18747
|
+
for (const mapped of mapPiEvent(event, piSessionId ?? "", mapperState, contextWindow)) {
|
|
18739
18748
|
turn.push(mapped);
|
|
18740
18749
|
if (mapped.kind === "turn-end") turn.close();
|
|
18741
18750
|
}
|
|
@@ -18784,6 +18793,7 @@ function createAgentCliClient(definition) {
|
|
|
18784
18793
|
async connect(opts) {
|
|
18785
18794
|
onActivity = opts.onActivity;
|
|
18786
18795
|
connectEffort = opts.effort;
|
|
18796
|
+
contextWindow = resolvePiContextWindow(opts.model);
|
|
18787
18797
|
const args = ["--mode", "rpc"];
|
|
18788
18798
|
if (opts.model) args.push("--model", opts.model);
|
|
18789
18799
|
if (opts.resumeSessionId) args.push("--session", opts.resumeSessionId);
|