bermudis-pi-goodies 0.27.1 → 0.27.2
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/README.md +8 -5
- package/package.json +1 -1
- package/side.ts +97 -5
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ extensions. One entry point, thirteen independent features.
|
|
|
26
26
|
First install, pinned to a published version:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
pi install npm:bermudis-pi-goodies@0.27.
|
|
29
|
+
pi install npm:bermudis-pi-goodies@0.27.2
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Picking up a new release on an existing install (`pi install` is not the
|
|
@@ -438,10 +438,13 @@ Details worth knowing:
|
|
|
438
438
|
path. (`deliverAs: "nextTurn"` would only queue in memory and die with
|
|
439
439
|
the process.)
|
|
440
440
|
- The footer's context-usage estimate reflects the raw session branch, not
|
|
441
|
-
the lens output, so it over-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
441
|
+
the lens output, so it can over-report while a side session is active.
|
|
442
|
+
Threshold auto-compaction is vetoed when the **lensed side request**
|
|
443
|
+
fits the side model's window (including system/tool overhead and the
|
|
444
|
+
configured token reserve), rather than trusting the main model's usage.
|
|
445
|
+
The quoted main transcript still counts toward that window. Manual
|
|
446
|
+
`/compact` and actual context-overflow recovery remain available.
|
|
447
|
+
Compaction summaries that cover side
|
|
445
448
|
turns are quoted with an explicit who-is-who caveat instead of passing as
|
|
446
449
|
the consultant's own history.
|
|
447
450
|
- Both quote directions carry an explicit untrusted-evidence preamble:
|
package/package.json
CHANGED
package/side.ts
CHANGED
|
@@ -28,11 +28,10 @@
|
|
|
28
28
|
* limb that is ever resumed delivers only newer turns. v1 always starts a
|
|
29
29
|
* fresh side limb, but the bookkeeping keeps that future-safe.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* is compaction-aware and survives it; the main limb is never touched).
|
|
31
|
+
* Pi's footer context-usage estimate reflects the raw session branch, not
|
|
32
|
+
* the lens output, so the footer over-reports during side sessions. A
|
|
33
|
+
* session_before_compact guard vetoes threshold compaction when the lensed
|
|
34
|
+
* request fits; manual compaction and actual overflow recovery still run.
|
|
36
35
|
* The side badge carries pi's own estimator applied to the lensed messages
|
|
37
36
|
* ("· ~46k lensed") as the honest counterpoint to the inflated figure.
|
|
38
37
|
*/
|
|
@@ -44,11 +43,14 @@ import type {
|
|
|
44
43
|
ExtensionCommandContext,
|
|
45
44
|
ExtensionContext,
|
|
46
45
|
SessionEntry,
|
|
46
|
+
ToolInfo,
|
|
47
47
|
} from "@earendil-works/pi-coding-agent";
|
|
48
48
|
import {
|
|
49
49
|
ModelSelectorComponent,
|
|
50
|
+
calculateContextTokens,
|
|
50
51
|
estimateTokens,
|
|
51
52
|
sessionEntryToContextMessages,
|
|
53
|
+
shouldCompact,
|
|
52
54
|
} from "@earendil-works/pi-coding-agent";
|
|
53
55
|
import { describeError, extractTextParts } from "./json-file.ts";
|
|
54
56
|
import { buildTrajectory, renderTrajectory } from "./copy-trajectory.ts";
|
|
@@ -321,6 +323,50 @@ export function estimateLensedContextTokens(
|
|
|
321
323
|
return messages.reduce((sum, message) => sum + estimateTokens(message), 0);
|
|
322
324
|
}
|
|
323
325
|
|
|
326
|
+
/**
|
|
327
|
+
* Conservative side-request budget. Main-model usage cannot leak into this
|
|
328
|
+
* estimate: its assistant turns have already become quoted user text.
|
|
329
|
+
* Include system/tool overhead before the first side response, and trust
|
|
330
|
+
* valid SIDE usage as a floor (it includes provider-visible overhead).
|
|
331
|
+
*/
|
|
332
|
+
export function estimateSideCompactionTokens(
|
|
333
|
+
messages: ContextEvent["messages"],
|
|
334
|
+
systemPrompt: string,
|
|
335
|
+
tools: readonly Pick<ToolInfo, "name" | "description" | "parameters">[],
|
|
336
|
+
rawBranch: readonly SessionEntry[],
|
|
337
|
+
): number {
|
|
338
|
+
// Kept responses still carry pre-compaction usage. Match by source
|
|
339
|
+
// message identity, not timestamp: branch order is the provenance Pi uses.
|
|
340
|
+
const lastInvalidation = rawBranch.findLastIndex(
|
|
341
|
+
(entry) => entry.type === "compaction" || entry.type === "context_edit",
|
|
342
|
+
);
|
|
343
|
+
const validUsageMessages = new Set(
|
|
344
|
+
rawBranch
|
|
345
|
+
.slice(lastInvalidation + 1)
|
|
346
|
+
.flatMap((entry) => sessionEntryToContextMessages(entry)),
|
|
347
|
+
);
|
|
348
|
+
const estimated =
|
|
349
|
+
messages.reduce((sum, message) => sum + estimateTokens(message), 0) +
|
|
350
|
+
Math.ceil(systemPrompt.length / 4) +
|
|
351
|
+
Math.ceil(JSON.stringify(tools).length / 4);
|
|
352
|
+
let trailing = 0;
|
|
353
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
354
|
+
const message = messages[i]!;
|
|
355
|
+
if (
|
|
356
|
+
message.role === "assistant" &&
|
|
357
|
+
validUsageMessages.has(message) &&
|
|
358
|
+
message.stopReason !== "error" &&
|
|
359
|
+
message.stopReason !== "aborted" &&
|
|
360
|
+
message.usage
|
|
361
|
+
) {
|
|
362
|
+
const usage = calculateContextTokens(message.usage);
|
|
363
|
+
if (usage > 0) return Math.max(estimated, usage + trailing);
|
|
364
|
+
}
|
|
365
|
+
trailing += estimateTokens(message);
|
|
366
|
+
}
|
|
367
|
+
return estimated;
|
|
368
|
+
}
|
|
369
|
+
|
|
324
370
|
// ---------------------------------------------------------------------------
|
|
325
371
|
// Handoff: what the main agent is given at exit
|
|
326
372
|
// ---------------------------------------------------------------------------
|
|
@@ -627,6 +673,52 @@ let modelRegistryRef: ExtensionContext["modelRegistry"] | undefined;
|
|
|
627
673
|
let lastBadgeCtx: ExtensionContext | undefined;
|
|
628
674
|
|
|
629
675
|
export default function side(pi: ExtensionAPI): void {
|
|
676
|
+
// Pi checks the raw projection BEFORE emitting `context`. Recheck the
|
|
677
|
+
// same threshold against our lens at its supported cancellation boundary.
|
|
678
|
+
// Never veto manual compaction or a real provider overflow.
|
|
679
|
+
pi.on("session_before_compact", (event, ctx) => {
|
|
680
|
+
if (event.reason !== "threshold" || !ctx.model) return;
|
|
681
|
+
const raw = ctx.sessionManager.getBranch();
|
|
682
|
+
const markerIdx = findSideBoundary(raw);
|
|
683
|
+
const marker = raw[markerIdx];
|
|
684
|
+
if (marker?.type !== "custom" || !parseSideMarkerData(marker.data)) return;
|
|
685
|
+
const messages = buildLensMessages(
|
|
686
|
+
ctx.sessionManager.buildContextEntries(),
|
|
687
|
+
raw,
|
|
688
|
+
markerIdx,
|
|
689
|
+
[],
|
|
690
|
+
);
|
|
691
|
+
if (!messages || ctx.model.contextWindow <= 0) return;
|
|
692
|
+
const activeTools = new Set(pi.getActiveTools());
|
|
693
|
+
const tokens = estimateSideCompactionTokens(
|
|
694
|
+
messages,
|
|
695
|
+
ctx.getSystemPrompt(),
|
|
696
|
+
pi
|
|
697
|
+
.getAllTools()
|
|
698
|
+
.filter((tool) => activeTools.has(tool.name))
|
|
699
|
+
.map(({ name, description, parameters }) => ({
|
|
700
|
+
name,
|
|
701
|
+
description,
|
|
702
|
+
parameters,
|
|
703
|
+
})),
|
|
704
|
+
raw,
|
|
705
|
+
);
|
|
706
|
+
const needed = shouldCompact(
|
|
707
|
+
tokens,
|
|
708
|
+
ctx.model.contextWindow,
|
|
709
|
+
event.preparation.settings,
|
|
710
|
+
);
|
|
711
|
+
logGoodiesEvent({
|
|
712
|
+
type: "side_compaction_check",
|
|
713
|
+
raw_tokens: event.preparation.tokensBefore,
|
|
714
|
+
side_tokens: tokens,
|
|
715
|
+
context_window: ctx.model.contextWindow,
|
|
716
|
+
reserve_tokens: event.preparation.settings.reserveTokens,
|
|
717
|
+
cancelled: !needed,
|
|
718
|
+
});
|
|
719
|
+
if (!needed) return { cancel: true };
|
|
720
|
+
});
|
|
721
|
+
|
|
630
722
|
// Lens: rewrite side-agent requests so the main trajectory arrives as an
|
|
631
723
|
// attributed quote instead of the side model's own history. Detection uses
|
|
632
724
|
// the raw branch (the marker is always an ancestor there); content uses
|