pi-posthorse 0.10.7 → 0.10.9

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 CHANGED
@@ -23,7 +23,7 @@ Give Pi your task. For example:
23
23
 
24
24
  ## How rollover works
25
25
 
26
- Posthorse can nudge the model to update task notes before rollover, then continue working. The nudge does not require `new_context` or wake an agent after its final answer. Large turns can reach rollover before the nudge.
26
+ Posthorse can send a routine task-note reminder before rollover. The reminder tells the model to save current task state if needed, then immediately continue the prescribed work. The context window is not a task deadline. The reminder does not require `new_context` or wake an agent after its final answer. Large turns can reach rollover before the reminder. See [Reminder timing](docs/reminder-timing.md) for the trigger and its relationship to Pi's reserve and retention settings.
27
27
 
28
28
  Automatic rollover retains a safe recent-message tail using Pi's `compaction.keepRecentTokens`, alongside a bounded recovery record for discarded inputs and tool evidence. It never calls a summarization model. The model must restore notes and check live state before it continues. Optional `new_context` deliberately retains no prior messages; manual `/compact` uses Pi's normal summarization.
29
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-posthorse",
3
- "version": "0.10.7",
3
+ "version": "0.10.9",
4
4
  "type": "module",
5
5
  "description": "Posthorse: fresh context, same journey. No-summary context rollover for Pi, with sparse budget reminders, handoffs, notes, and history.",
6
6
  "keywords": [
package/src/budget.ts CHANGED
@@ -13,8 +13,13 @@ import {
13
13
  import { imagesOf, textOf } from "./message-text.ts";
14
14
 
15
15
  const MIN_USABLE_TOKENS = Math.ceil(MAX_HANDOFF_CHARS / 4) * 2;
16
- export const CHECKPOINT_STEPS =
17
- "update the task's current-state note (goal, progress, decisions, next steps) with available file-editing tools at its absolute path; use notes write only for creation, substantial restructuring, or when editing tools are unavailable. If the note is already current, leave it unchanged. Then continue working; let automatic rollover happen without calling new_context";
16
+ export const CHECKPOINT_GUIDANCE = `Keep working on the assigned task; Pi handles context rollover automatically.
17
+
18
+ The current context window is not a deadline for finishing your task. Do not reduce scope, skip prescribed steps or verification, or issue a final progress summary because rollover is approaching.
19
+
20
+ Update the task's current-state note if needed: goal, progress, decisions, and next steps. Use available file-editing tools at its absolute path; use notes write only for creation, substantial restructuring, or when editing tools are unavailable. Leave an already-current note unchanged. Then immediately execute the next prescribed task step in this run. No new_context call, waiting for rollover, or user "continue" is needed.
21
+
22
+ Finish when the requested work and required verification are complete, or report a genuine blocker. Approaching context rollover is not a blocker.`;
18
23
  export type Budget = {
19
24
  readonly contextWindow: number;
20
25
  readonly reserveTokens: number;
@@ -76,15 +81,11 @@ function automaticGuidance(budget: Budget | undefined, enabled: boolean): string
76
81
  if (budget !== undefined && !budget.supported) {
77
82
  return unsupportedMessage(budget);
78
83
  }
79
- const deadline =
80
- budget === undefined
81
- ? "the configured Pi context limit"
82
- : `${Math.max(1, Math.round((budget.rolloverAt / budget.contextWindow) * 100))}% used`;
83
84
  const line =
84
85
  budget === undefined
85
86
  ? ""
86
87
  : ` The rollover line is ${budget.rolloverAt.toLocaleString("en-US")} tokens used under the available Pi settings.`;
87
- return `Automatic rollover follows Pi's compaction setting and retains a safe recent-message tail using compaction.keepRecentTokens, without model summarization.${line} At most one notes reminder may arrive at the end of a working turn before the rollover line (${deadline}), never on a turn that ends the run; when it does, ${CHECKPOINT_STEPS}. new_context is an optional deliberate clean reset, not a checkpoint requirement.`;
88
+ return `${CHECKPOINT_GUIDANCE}\nAutomatic rollover follows Pi's compaction setting and retains a safe recent-message tail using compaction.keepRecentTokens, without model summarization.${line} At most one routine task-note reminder may arrive at the end of a working turn, never on a turn that ends the run. new_context is an optional deliberate clean reset, not a checkpoint requirement.`;
88
89
  }
89
90
  export function buildGuidance(ctx: PolicyContext): string {
90
91
  const budget = budgetFor(ctx);
@@ -93,10 +94,10 @@ export function buildGuidance(ctx: PolicyContext): string {
93
94
  budget === undefined
94
95
  ? "Configured context capacity is unknown."
95
96
  : `Configured context capacity: ${budget.contextWindow.toLocaleString("en-US")} tokens. This is Pi's best available native limit, not remaining space or a measured provider boundary. Fresh windows use the active configuration; system instructions, tools, the handoff, retained recent messages, and new messages consume part of it.`;
96
- return `## Context self-management (Posthorse)
97
+ return `## Automatic context management (Posthorse)
98
+ ${automaticGuidance(budget, enabled)}
97
99
  ${capacity}
98
100
  Do not guess context capacity or remaining space from response-token limits or reasoning budgets. Before reporting a remaining-token count or changing your work plan because of context limits, call get_context_remaining. If usage is unknown, report it as unknown. No routine budget checks are needed.
99
- ${automaticGuidance(budget, enabled)}
100
101
  After a rollover, earlier conversation stays in history. Restore notes and todos, then verify live state before stateful or external work; automatic handoffs record inputs, not progress.
101
102
  Keep one concise current-state note per task. Preserve decisions and safety constraints; link fuller evidence and history instead of copying them. Edit changed sections rather than resending unchanged content.
102
103
  Write handoffs and notes as normal readable prose; notes and history search match literal text.`;
package/src/rollover.ts CHANGED
@@ -5,7 +5,7 @@ import type {
5
5
  ExtensionContext,
6
6
  TurnEndEvent,
7
7
  } from "@earendil-works/pi-coding-agent";
8
- import { budgetFor, CHECKPOINT_STEPS, freshPayloadChars, type Budget } from "./budget.ts";
8
+ import { budgetFor, CHECKPOINT_GUIDANCE, freshPayloadChars, type Budget } from "./budget.ts";
9
9
  import {
10
10
  isRecord,
11
11
  isReminderType,
@@ -83,9 +83,7 @@ function endsRun(
83
83
  ctx.hasPendingMessages?.() !== true
84
84
  );
85
85
  }
86
- function reminderBudget(
87
- host: PolicyContext,
88
- ): { readonly budget: Budget; readonly tokens: number } | undefined {
86
+ function reminderBudget(host: PolicyContext): Budget | undefined {
89
87
  const usage = host.getContextUsage();
90
88
  if (usage === undefined || usage.tokens === null || usage.contextWindow <= 0) {
91
89
  return undefined;
@@ -100,14 +98,13 @@ function reminderBudget(
100
98
  return undefined;
101
99
  }
102
100
  const buffer = Math.min(REMINDER_BUFFER_TOKENS, Math.floor(budget.usable * 0.1));
103
- return usage.tokens < budget.rolloverAt - buffer ? undefined : { budget, tokens: usage.tokens };
101
+ return usage.tokens < budget.rolloverAt - buffer ? undefined : budget;
104
102
  }
105
103
  function sendReminder(
106
104
  pi: ExtensionAPI,
107
105
  ctx: ExtensionContext,
108
106
  available: {
109
107
  readonly budget: Budget;
110
- readonly tokens: number;
111
108
  readonly pendingMessages: readonly {
112
109
  readonly role: string;
113
110
  readonly customType?: string | undefined;
@@ -116,7 +113,7 @@ function sendReminder(
116
113
  },
117
114
  reminders: ReminderLookup,
118
115
  ): void {
119
- const { budget, tokens } = available;
116
+ const { budget } = available;
120
117
  const state = reminders.read(ctx);
121
118
  const fingerprint: ReminderFingerprint = {
122
119
  windowId: state.windowId,
@@ -137,7 +134,7 @@ function sendReminder(
137
134
  pi.sendMessage(
138
135
  {
139
136
  customType: REMINDER_TYPE,
140
- content: `[posthorse] Notes checkpoint: ${(budget.rolloverAt - tokens).toLocaleString("en-US")} tokens remain before Pi's automatic rollover line. Please ${CHECKPOINT_STEPS}.`,
137
+ content: `[posthorse] Routine task-note update.\n${CHECKPOINT_GUIDANCE}`,
141
138
  display: true,
142
139
  details: fingerprint,
143
140
  },
@@ -481,7 +478,7 @@ export function registerRollover(
481
478
  sendReminder(
482
479
  pi,
483
480
  ctx,
484
- { ...available, pendingMessages: event.context.pendingMessages },
481
+ { budget: available, pendingMessages: event.context.pendingMessages },
485
482
  reminders,
486
483
  );
487
484
  }