@astrosheep/pi-context 0.3.0 → 0.4.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 +2 -2
- package/package.json +1 -1
- package/src/index.ts +24 -27
package/README.md
CHANGED
|
@@ -19,8 +19,8 @@ pi -e npm:@astrosheep/pi-context
|
|
|
19
19
|
The extension composes Pi's public `session_before_compact` / `session_compact` hooks, custom session entries, and the `context` hook to approximate Codex's experimental context management:
|
|
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
|
-
- **`<context_window>` hint** —
|
|
23
|
-
- **Low-budget guidance** —
|
|
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** — while remaining context is at or below 16,000 tokens, a `<context_window_guidance>` reminder is injected transiently on every request (transient single-shot would vanish from the next request; Codex's persisted reminder stays visible, so continuous low-budget injection is the effective parity). It tells the model to persist state with `notes_write_file` and call `new_context` before the window closes. 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
|
@@ -4,6 +4,7 @@ import { defineTool, type ExtensionAPI, type ExtensionContext } from "@earendil-
|
|
|
4
4
|
|
|
5
5
|
const STATE_TYPE = "pi-context/state";
|
|
6
6
|
const NOTE_TYPE = "pi-context/note";
|
|
7
|
+
const HINT_TYPE = "pi-context/hint";
|
|
7
8
|
const RESET_MARKER_TYPE = "pi-context/reset-marker";
|
|
8
9
|
const CONTINUATION_TYPE = "pi-context/continuation";
|
|
9
10
|
const MAX_NOTE_BYTES = 1_000_000;
|
|
@@ -239,11 +240,6 @@ function tokenBudgetGuidance(remaining: number): string {
|
|
|
239
240
|
return `${GUIDANCE_OPEN_TAG}\nYou have ${remaining} tokens left in this context window. Write durable state with notes_write_file and call new_context before the window closes.\n${GUIDANCE_CLOSE_TAG}`;
|
|
240
241
|
}
|
|
241
242
|
|
|
242
|
-
function currentWindowId(ctx: ExtensionContext): string | undefined {
|
|
243
|
-
const windows = historyFromSession(ctx);
|
|
244
|
-
return windows[windows.length - 1]?.windowId;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
243
|
function lineRange(text: string, startValue: unknown, stopValue: unknown) {
|
|
248
244
|
const lines = text.split("\n");
|
|
249
245
|
const resolve = (value: unknown, fallback: number) => {
|
|
@@ -264,8 +260,17 @@ const role = Type.Union([Type.Literal("user"), Type.Literal("assistant"), Type.L
|
|
|
264
260
|
|
|
265
261
|
export default function piContext(pi: ExtensionAPI) {
|
|
266
262
|
let rollover: "idle" | "requested" | "compacting" | "continued" = "idle";
|
|
267
|
-
let reminderClaimedInWindow: string | undefined;
|
|
268
263
|
let enabled = true;
|
|
264
|
+
|
|
265
|
+
/** Persist the context_window hint as a visible message (lands in history and the TUI), Codex-style. */
|
|
266
|
+
const persistHint = (ctx: ExtensionContext) => {
|
|
267
|
+
pi.sendMessage({ customType: HINT_TYPE, content: contextWindowHint(ctx), display: true }, { triggerTurn: false });
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
pi.on("session_start", (_event, ctx) => {
|
|
271
|
+
if (!enabled) return;
|
|
272
|
+
persistHint(ctx);
|
|
273
|
+
});
|
|
269
274
|
const saveNote = (op: NoteOperation) => {
|
|
270
275
|
// pi.appendEntry writes a custom SessionManager entry. Custom entries are persistent but excluded from LLM context.
|
|
271
276
|
// ExtensionContext deliberately exposes only a readonly SessionManager, so this is the public extension write path.
|
|
@@ -409,27 +414,18 @@ export default function piContext(pi: ExtensionAPI) {
|
|
|
409
414
|
|
|
410
415
|
pi.on("context", (event, ctx) => {
|
|
411
416
|
if (!enabled) return undefined;
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
const
|
|
417
|
+
// Transient per-request while below the threshold. Codex's reminder persists in
|
|
418
|
+
// history and therefore stays visible; re-injecting while low is the effective parity.
|
|
419
|
+
const usage = ctx.getContextUsage();
|
|
420
|
+
if (!usage || usage.tokens === null) return undefined;
|
|
421
|
+
const remaining = Math.max(0, usage.contextWindow - usage.tokens);
|
|
422
|
+
if (remaining > REMINDER_THRESHOLD_TOKENS) return undefined;
|
|
423
|
+
const guidance = {
|
|
415
424
|
role: "user" as const,
|
|
416
|
-
content: [{ type: "text" as const, text }],
|
|
425
|
+
content: [{ type: "text" as const, text: tokenBudgetGuidance(remaining) }],
|
|
417
426
|
timestamp: Date.now(),
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
// Codex token_budget.maybe_record parity: below the threshold, claim the
|
|
422
|
-
// reminder once per context window; a new window makes it eligible again.
|
|
423
|
-
const usage = ctx.getContextUsage();
|
|
424
|
-
if (usage && usage.tokens !== null) {
|
|
425
|
-
const remaining = Math.max(0, usage.contextWindow - usage.tokens);
|
|
426
|
-
const windowId = currentWindowId(ctx);
|
|
427
|
-
if (remaining <= REMINDER_THRESHOLD_TOKENS && reminderClaimedInWindow !== windowId) {
|
|
428
|
-
reminderClaimedInWindow = windowId;
|
|
429
|
-
injected.push(userText(tokenBudgetGuidance(remaining)));
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
return { messages: [...injected, ...event.messages] };
|
|
427
|
+
};
|
|
428
|
+
return { messages: [guidance, ...event.messages] };
|
|
433
429
|
});
|
|
434
430
|
|
|
435
431
|
pi.registerTool(defineTool({
|
|
@@ -480,13 +476,14 @@ export default function piContext(pi: ExtensionAPI) {
|
|
|
480
476
|
}
|
|
481
477
|
});
|
|
482
478
|
|
|
483
|
-
pi.on("session_compact", (event) => {
|
|
479
|
+
pi.on("session_compact", (event, ctx) => {
|
|
484
480
|
if (!enabled) return;
|
|
485
481
|
// Overflow retry is already continued once by Pi core. Sending another turn would duplicate it.
|
|
486
482
|
if (event.willRetry) return;
|
|
487
483
|
if (rollover !== "compacting") return;
|
|
488
484
|
rollover = "continued";
|
|
489
485
|
pi.appendEntry(STATE_TYPE, { version: 1, lastResetEntryId: event.compactionEntry.id });
|
|
486
|
+
persistHint(ctx);
|
|
490
487
|
pi.sendMessage({ customType: CONTINUATION_TYPE, content: CONTINUATION, display: false }, { triggerTurn: true });
|
|
491
488
|
});
|
|
492
489
|
|
|
@@ -495,4 +492,4 @@ export default function piContext(pi: ExtensionAPI) {
|
|
|
495
492
|
});
|
|
496
493
|
}
|
|
497
494
|
|
|
498
|
-
export const internal = { MAX_NOTE_BYTES, NOTE_TYPE, RESET_MARKER_TYPE, RESET_SUMMARY, CONTINUATION, CONTEXT_WINDOW_OPEN_TAG, GUIDANCE_OPEN_TAG, REMINDER_THRESHOLD_TOKENS, lineRange, assertVirtualPath };
|
|
495
|
+
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 };
|