@astrosheep/pi-context 0.4.1 → 0.5.0
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 +1 -1
- package/package.json +1 -1
- package/src/index.ts +27 -9
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ The extension composes Pi's public `session_before_compact` / `session_compact`
|
|
|
20
20
|
|
|
21
21
|
- **`new_context` tool** — the model requests a fresh context window. The extension waits for the current tool turn to end, compacts with a short deterministic reset message (old conversation is excluded from the new provider context but stays in the session), then sends exactly one hidden continuation turn.
|
|
22
22
|
- **`<context_window>` hint** — persisted as a visible custom message at session start and after each window reset (Codex-style: written once into history instead of re-injected per request). It carries the agent name, first/current/previous window IDs, and the 5 most recently updated notes. Within a window the note list goes stale, exactly like Codex's steady-state world-state diffing.
|
|
23
|
-
- **Low-budget guidance** —
|
|
23
|
+
- **Low-budget guidance** — when remaining context first drops to 16,000 tokens or below, a `<context_window_guidance>` reminder is **persisted once per window** into history (TUI-visible, no extra turn; `sendMessage` safely defers to end of turn mid-stream). The in-flight request additionally gets one transient tail copy so the model sees it immediately. Text is static and appended, so the provider prefix cache survives. The exact remaining figure is one `get_context_remaining` call away. Note: Pi's built-in auto-compaction fires when remaining context falls below `reserveTokens` (default 16,384), so raise the reminder threshold above your `reserveTokens` or the reminder never precedes compaction.
|
|
24
24
|
- **Runtime toggle** — `/pi-context off` disables hint injection, guidance, and reset-style compaction (Pi's default compaction, including `keepRecentTokens`, applies again). `/pi-context on` re-enables; a bare `/pi-context` reports the current state.
|
|
25
25
|
- **History tools** — the model searches pre-reset conversation with case-sensitive literal substring search, exactly like Codex's `history.*` namespace.
|
|
26
26
|
- **Notes tools** — persistent, session-scoped virtual files that survive window resets.
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { defineTool, type ExtensionAPI, type ExtensionContext } from "@earendil-
|
|
|
5
5
|
const STATE_TYPE = "pi-context/state";
|
|
6
6
|
const NOTE_TYPE = "pi-context/note";
|
|
7
7
|
const HINT_TYPE = "pi-context/hint";
|
|
8
|
+
const GUIDANCE_TYPE = "pi-context/guidance";
|
|
8
9
|
const RESET_MARKER_TYPE = "pi-context/reset-marker";
|
|
9
10
|
const CONTINUATION_TYPE = "pi-context/continuation";
|
|
10
11
|
const MAX_NOTE_BYTES = 1_000_000;
|
|
@@ -236,14 +237,24 @@ export function contextWindowHint(ctx: ExtensionContext): string {
|
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
/**
|
|
239
|
-
* Codex-equivalent low-budget reminder. Static by design:
|
|
240
|
-
*
|
|
241
|
-
* the provider's prefix cache on each call. The exact figure is one tool call away.
|
|
240
|
+
* Codex-equivalent low-budget reminder. Static by design: a stale token count in a
|
|
241
|
+
* persisted message would mislead later turns; the exact figure is one tool call away.
|
|
242
242
|
*/
|
|
243
243
|
function tokenBudgetGuidance(): string {
|
|
244
244
|
return `${GUIDANCE_OPEN_TAG}\nContext budget is at or below ${REMINDER_THRESHOLD_TOKENS} tokens remaining. Persist durable state with notes_write_file, then call new_context before the window closes. get_context_remaining reports the exact figure.\n${GUIDANCE_CLOSE_TAG}`;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
/** Cheap current-window lookup: scan the branch tail for the latest compaction entry. */
|
|
248
|
+
function currentWindowId(ctx: ExtensionContext): string {
|
|
249
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
250
|
+
const branch = ctx.sessionManager.getBranch();
|
|
251
|
+
for (let i = branch.length - 1; i >= 0; i--) {
|
|
252
|
+
const entry = branch[i];
|
|
253
|
+
if (entry?.type === "compaction") return `pcw:${sessionId}:${entry.id}`;
|
|
254
|
+
}
|
|
255
|
+
return `pcw:${sessionId}:root`;
|
|
256
|
+
}
|
|
257
|
+
|
|
247
258
|
function lineRange(text: string, startValue: unknown, stopValue: unknown) {
|
|
248
259
|
const lines = text.split("\n");
|
|
249
260
|
const resolve = (value: unknown, fallback: number) => {
|
|
@@ -265,6 +276,7 @@ const role = Type.Union([Type.Literal("user"), Type.Literal("assistant"), Type.L
|
|
|
265
276
|
export default function piContext(pi: ExtensionAPI) {
|
|
266
277
|
let rollover: "idle" | "requested" | "compacting" | "continued" = "idle";
|
|
267
278
|
let enabled = true;
|
|
279
|
+
let guidancePersistedInWindow: string | undefined;
|
|
268
280
|
|
|
269
281
|
/** Persist the context_window hint as a visible message (lands in history and the TUI), Codex-style. */
|
|
270
282
|
const persistHint = (ctx: ExtensionContext) => {
|
|
@@ -418,17 +430,23 @@ export default function piContext(pi: ExtensionAPI) {
|
|
|
418
430
|
|
|
419
431
|
pi.on("context", (event, ctx) => {
|
|
420
432
|
if (!enabled) return undefined;
|
|
421
|
-
// Transient while below the threshold: Codex's reminder persists in history and
|
|
422
|
-
// stays visible, so continuous low-budget visibility is the effective parity.
|
|
423
|
-
// Appended (not prepended) with static text: the cached prefix stays untouched,
|
|
424
|
-
// crossing the threshold only adds one stable suffix segment.
|
|
425
433
|
const usage = ctx.getContextUsage();
|
|
426
434
|
if (!usage || usage.tokens === null) return undefined;
|
|
427
435
|
const remaining = Math.max(0, usage.contextWindow - usage.tokens);
|
|
428
436
|
if (remaining > REMINDER_THRESHOLD_TOKENS) return undefined;
|
|
437
|
+
const windowId = currentWindowId(ctx);
|
|
438
|
+
if (guidancePersistedInWindow === windowId) return undefined;
|
|
439
|
+
guidancePersistedInWindow = windowId;
|
|
440
|
+
// Persist once per window, like the hint. sendMessage defers safely to end of
|
|
441
|
+
// turn while streaming (sendCustomMessage queues instead of splitting a tool
|
|
442
|
+
// call/result pair), so from the next turn on the guidance lives in history
|
|
443
|
+
// and the TUI. A transient tail copy covers the in-flight request; it is
|
|
444
|
+
// appended, static, and one-shot, so the cached prefix survives.
|
|
445
|
+
const text = tokenBudgetGuidance();
|
|
446
|
+
pi.sendMessage({ customType: GUIDANCE_TYPE, content: text, display: true }, { triggerTurn: false });
|
|
429
447
|
const guidance = {
|
|
430
448
|
role: "user" as const,
|
|
431
|
-
content: [{ type: "text" as const, text
|
|
449
|
+
content: [{ type: "text" as const, text }],
|
|
432
450
|
timestamp: Date.now(),
|
|
433
451
|
};
|
|
434
452
|
return { messages: [...event.messages, guidance] };
|
|
@@ -498,4 +516,4 @@ export default function piContext(pi: ExtensionAPI) {
|
|
|
498
516
|
});
|
|
499
517
|
}
|
|
500
518
|
|
|
501
|
-
export const internal = { MAX_NOTE_BYTES, NOTE_TYPE, HINT_TYPE, RESET_MARKER_TYPE, RESET_SUMMARY, CONTINUATION, CONTEXT_WINDOW_OPEN_TAG, GUIDANCE_OPEN_TAG, REMINDER_THRESHOLD_TOKENS, lineRange, assertVirtualPath };
|
|
519
|
+
export const internal = { MAX_NOTE_BYTES, NOTE_TYPE, HINT_TYPE, GUIDANCE_TYPE, RESET_MARKER_TYPE, RESET_SUMMARY, CONTINUATION, CONTEXT_WINDOW_OPEN_TAG, GUIDANCE_OPEN_TAG, REMINDER_THRESHOLD_TOKENS, lineRange, assertVirtualPath };
|