pi-fold 1.0.2 → 1.0.3

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
@@ -18,9 +18,11 @@ pi install npm:pi-fold
18
18
 
19
19
  Node 22 or later, Pi 0.83 or later. Licensed under [MIT](./LICENSE).
20
20
 
21
+ pi-fold serves the working tier of a four-tier memory stack. [pi-canon](https://github.com/shaneconner/pi-canon) is a separate, optional package serving the long-term tiers, the journal episodic and the canon semantic; the two compose but neither requires the other.
22
+
21
23
  ## What it is
22
24
 
23
- pi-fold registers one tool, `active_context`, and gives the session's own agent nine actions over its transcript: `status`, `peek`, `fold`, `expand`, `refold`, `protect`, `unprotect`, `rebrief`, and `reboundary`, plus `unmark` under epoch scheduling. It also registers `/context` and `/fold-context` for the human.
25
+ pi-fold registers one tool, `pi_fold_context`, and gives the session's own agent nine actions over its transcript: `status`, `peek`, `fold`, `expand`, `refold`, `protect`, `unprotect`, `rebrief`, and `reboundary`, plus `unmark` under epoch scheduling. It also registers `/pi-fold-context` and `/fold-context` for the human.
24
26
 
25
27
  A fold takes a contiguous span of session entries and replaces it, in the window only, with a short brief. The entries themselves go to a fold store, byte for byte, addressed by their SHA-256 hash. The brief carries a handle, the handle resolves to the original, and expansion restores the exact bytes after verifying the hash. A fold is therefore a claim the runtime can check, not a claim the reader has to trust.
26
28
 
@@ -30,13 +32,13 @@ Everything else follows from one invariant, and it is the reason several otherwi
30
32
 
31
33
  **So the runtime may move a byte in the window only at a moment it is already rewriting the window**, which is a commit or a fold. Everything else is a pure append at the tail, and an appended byte is never later altered, shortened, removed, or repositioned. Stated plainly: never mutate the window, and never show something and then take it back. The second half is the one that costs features. Anything that displays state and then updates it in place is out, however useful the display was, because updating in place is a mid-prefix write with better manners.
32
34
 
33
- **Marks are free, because a mark moves nothing.** Under `foldScheduling: "epoch"` a `fold` action records a pending mark and returns immediately. The mark lives in durable session state, outside the window; the projection stays byte-identical and the cached prefix survives. The agent can therefore mark as it works, the moment a span stops mattering, without weighing each fold against a cache invalidation.
35
+ **Marks are free, because a mark moves nothing.** A `fold` action records a pending mark and returns immediately. The mark lives in durable session state, outside the window; the projection stays byte-identical and the cached prefix survives. The agent can therefore mark as it works, the moment a span stops mattering, without weighing each fold against a cache invalidation.
34
36
 
35
- **A commit is the one moment bytes move, and every accumulated decision lands at once.** It goes through the same preparation and commit machinery an immediate fold uses, so a committed mark and an immediate fold produce the identical fold record. Committing is the runtime's call, not a verb the agent can spend: measured 2026-08-07, an agent given the verb called it twice and the runtime correctly held it both times, which is surface without function.
37
+ **A commit is the one moment bytes move, and every accumulated decision lands at once.** It goes through the preparation and commit machinery every fold has always used, so a committed mark produces the ordinary fold record. Committing is the runtime's call, not a verb the agent can spend: measured 2026-08-07, an agent given the verb called it twice and the runtime correctly held it both times, which is surface without function.
36
38
 
37
39
  **Folds are lossless, and there are two ways back in.** Folds form a DAG whose leaves are the exact session entries, and every fold record keeps ordered references down to those leaves. `peek` is the cheap read: it returns one fold's exact SHA-256-verified source as a tool result, at any depth, with the ancestors still collapsed, without touching the projection. It takes `offset` and `bytes` for a bounded slice, and any child fold id is peekable, so a large fold has a narrow read. `expand` is the commitment, restoring that source in place until the fold is refolded, outside-in and one level at a time. Looking is not the same as taking, and most of the time an agent needs to check one detail rather than reopen an hour of work.
38
40
 
39
- When the agent does not act, an automatic ladder manages the window the same lossless way, and it is deliberately more conservative: completed read-only tool batches, structurally closed chapters, and consolidation of existing folds only past explicit thresholds. Only the oldest eligible context changes; the fresh window is never touched. A session that never calls the tool degrades gracefully into lossless hierarchical compaction.
41
+ When the agent does not act, an automatic ladder manages the window the same lossless way, and it is deliberately more conservative: completed tool batches, structurally closed chapters, and consolidation of existing folds only past explicit thresholds. Only the oldest eligible context changes; the fresh window is never touched. A session that never calls the tool degrades gracefully into lossless hierarchical compaction.
40
42
 
41
43
  ## Folding against compaction
42
44
 
@@ -104,7 +106,7 @@ The measurement is written up as a paper, with the figure sources, the redacted
104
106
  - **The agent never called the tool.** All 62 folds in that run were the automatic ladder's; every commit records zero agent marks. So these are the fallback's numbers, produced by the part of the design that is closest to prior work, with agent curation, the distinguishing claim, unexercised.
105
107
  - **The reread story is a count, not a duplicate-payload metric.** It rests on file read count and bytes from disk, both in the tables above.
106
108
 
107
- Full method, artifact hashes and per-iteration history: [docs/fold_vs_compaction/experiment-log.md](https://github.com/shaneconner/pi-fold/blob/main/docs/fold_vs_compaction/experiment-log.md). Queued work, including the open problem the results end on: [docs/next-steps.md](https://github.com/shaneconner/pi-fold/blob/main/docs/next-steps.md).
109
+ Full method, artifact hashes and per-iteration history: [docs/fold_vs_compaction/experiment-log.md](https://github.com/shaneconner/pi-fold/blob/main/docs/fold_vs_compaction/experiment-log.md). Queued work: [docs/next-steps.md](https://github.com/shaneconner/pi-fold/blob/main/docs/next-steps.md).
108
110
 
109
111
  ## How it works
110
112
 
@@ -114,37 +116,37 @@ The agent's own actions come first. Through the context tool it folds any struct
114
116
 
115
117
  Every fold, agent-made or automatic, obeys the same structural rules. Folds nest rather than partially overlap. Chapter folds align to closed user, assistant and tool-batch units, consolidations take two or more adjacent folds, and protected or fresh evidence never folds.
116
118
 
117
- The ladder's thresholds are explicit constants rather than knobs. It folds only completed read-only tool batches and structurally closed chapters, absorbs only the tool-result folds inside a chapter's own span, and creates folds of folds past more than ten visible collapsed folds, which keeps the brief index tidy long before pressure matters, or past 85% measured occupancy of the serving budget, at which point consolidation squeezes existing folds to reclaim room. Briefs are bounded at 1,200 characters each, so the visible index normally costs about 1% of the window. Cadence and fresh-tail rules prevent repeated or premature automatic folding.
119
+ The automatic law folds only completed tool batches and structurally closed chapters, absorbs only the tool-result folds inside a chapter's own span, and treats fold placeholders as ordinary span material once the window carries `consolidateAfter` unpinned folds, which keeps the brief index tidy long before pressure matters. Briefs are bounded at 1,200 characters each, so the visible index normally costs about 1% of the window. Automatic foldability is membership, not position: a span folds because it is a completed tool batch or older material a chapter can compose, and what holds it back is the agent's pins, the guaranteed-raw fresh tail, the blacklist, and the commit-time guard on the open turn.
120
+
121
+ Below `maxTarget` occupancy of the serving budget, 80% by default, the runtime is quiet: nothing folds automatically at all, because a commit is an epoch transition that risks the whole prefix cache, and the cadence has to be the fewest commits that still keep the window inside its budget. The remaining fifth of the budget is the runway the commit itself spends. Every occupancy number in this document is a share of that one serving budget: there is a single denominator, `capacityAccounting.budgetTokens`, and no threshold is stated against the raw provider window.
118
122
 
119
- Below 80% occupancy of the serving budget the runtime is quiet: nothing folds automatically at all, because a commit is an epoch transition that risks the whole prefix cache, and the cadence has to be the fewest commits that still keep the window inside its budget. The remaining fifth of the budget is the runway the commit itself spends.
123
+ Chapter briefs are generated by the session's model by default, using one request per fold with a bounded input span, bounded output, and no tool calls. Every summarizer failure falls back to a deterministic brief, automatically and with no setting involved. The default spends provider tokens, as native compaction does. A cheaper model from the same provider is the recommended summarizer: a session on GPT 5.6 Sol can delegate fold briefs to GPT 5.6 Luna at medium effort with `summarizer: { provider: "openai-codex", model: "gpt-5.6-luna", effort: "medium" }`, using the provider id from your Pi model registry.
120
124
 
121
- Chapter briefs are generated by the session's model by default, using one request per fold with a bounded input span, bounded output, and no tool calls. Every summarizer failure falls back to a deterministic brief; set `summarizer: "deterministic"` to disable model calls entirely. The default spends provider tokens, as native compaction does. A cheaper model from the same provider is the recommended summarizer: a session on GPT 5.6 Sol can delegate fold briefs to GPT 5.6 Luna at medium effort with `summarizer: { provider: "openai", model: "gpt-5.6-luna", effort: "medium" }`, using the provider id from your Pi model registry.
125
+ The serving budget is what the deployment states it may fill, because the per-request max-input descriptor a provider advertises assumes a full output reservation and understates the real ceiling. With no `providerInputBudget` supplied the runtime falls back to the transport's descriptor and estimates a reservation out of it, which is the conservative default for a model it has no budget fact for; the descriptor is reported in status either way, so the gap stays auditable. A provider-window fence aborts an unsafe request before transmission if no lossless fold can make room, and says so out of band rather than by writing into the window.
122
126
 
123
- The serving budget is the total window less the output reservation actually in force, because the per-request max-input descriptor a provider advertises assumes a full reservation and understates the real ceiling. With no `providerTotalWindow` supplied the runtime falls back to the transport's descriptor, which is the conservative default for a model it has no total-window fact for; the descriptor is reported in status either way, so the gap stays auditable. A provider-window fence aborts an unsafe request before transmission if no lossless fold can make room, and says so out of band rather than by writing into the window.
127
+ Pi's automatic compaction stays enabled underneath pi-fold; there is no setting to turn off. The runtime intercepts every compaction pass and answers it by kind. A threshold pass is cancelled outright, because the fold commit at `maxTarget` fires first and folding remains authoritative. An overflow pass, the one Pi starts when the provider rejects a request as too large and is prepared to retry, becomes a recovery in the shape of `/tree`, automated: the failed path is labeled with its lineage, the session branches back to the last message before the error, and the retried pass folds and commits before it transmits, so the re-issued message fits inside a window that kept its folds and its exact originals. The agent gets one notice that a rollback and commit happened. A trailing assistant message still holding unanswered tool calls is not replayed, and the notice says so. Manual `/compact` passes through untouched as the user's escape hatch. Turning Pi's automatic compaction off removes the hook this lane arms on: the runtime still classifies an overflow when it appears, but reports that it cannot roll back rather than guessing at a tree whose agent state disagrees with it.
124
128
 
125
- By default, evidence ingestion writes read-only artifact files under the session directory's `pi-fold-evidence/`, sets them to mode `0444`, and enforces a 512 MB session cap. These immutable files provide exact-recovery anchors for oversized tool results. Set `evidenceIngestion: false` to disable the hook and all evidence writes.
129
+ Evidence ingestion writes read-only artifact files under the session directory's `pi-fold-evidence/`, sets them to mode `0444`, and enforces a 512 MB session cap. These immutable files provide exact-recovery anchors for oversized tool results. It is always on and there is no setting to turn it off: the artifacts are what an oversized result folds against, so a deployment that stopped writing them would keep its folds and lose what makes them lossless.
126
130
 
127
131
  ## Epoch fold scheduling
128
132
 
129
133
  Folding is cheap to decide and expensive to apply, and the expense has nothing to do with how much a fold saves. The bill is set by how often the projection changes, not by how many tokens each change reclaims. A session that folds once per turn re-sends its prefix once per turn; a session that folds three times in total re-sends it three times.
130
134
 
131
- `foldScheduling: "epoch"` splits folding into the two phases described above: a `fold` action records a free pending mark, `unmark` withdraws a standing decision, and a commit applies every pending mark in one projection rewrite. `"immediate"` remains the default, writing no pending-marks key and leaving existing state digests where they are.
135
+ Epoch scheduling is the only scheduler, and it splits folding into the two phases described above: a `fold` action records a free pending mark, `unmark` withdraws a standing decision, and a commit applies every pending mark in one projection rewrite. Applying each fold at the moment it was made shipped as an option through 1.0.2 and was deleted once measured: on the pairing below it cost 54 prefix rewrites where epoch paid 3.
132
136
 
133
137
  Two runs from the campaign put a number on the difference. This is the second of the two experiments: pi-fold against itself, both runs on gpt-5.6-luna, same plan, same seed, same configuration with fold scheduling as the only variable. It is internally controlled, and its numbers are not comparable across to the gpt-5.6-sol pairings above. Applying folds immediately spent 19,623,502 fresh input tokens across 105 requests at a pooled cache share of 0.119. Recording marks and landing them together spent 3,603,440 across 99 requests at 0.756: a 5.4x reduction in the tokens the provider had to read for the first time, carrying the total from 22,316,589 tokens down to 14,818,833. Epoch was slower in wall clock on that pairing, 33.5 minutes against 26.7, so the trade bought tokens and not time, and it is one run per arm rather than a rate.
134
138
 
135
- The agent is meant to lead here: mark as you work, and let the runtime pick the seam. The ladder is the fallback, exactly as it is for folding itself. Its own rung decisions become marks instead of folds, and it commits the epoch when its two signals agree, occupancy at 80% of the serving budget and enough stale tool mass outside the fresh tail for a commit to have something to work with, or under the pressure backstop. Each automatic commit carries a target: if the agent's marks would free less than a fifth of the window, the ladder tops the epoch up with the stalest unprotected completed tool batches until they do, and the commit result records which marks were the agent's and which were the top-up.
139
+ The agent is meant to lead here: mark as you work, and let the runtime pick the seam. The automatic law is the fallback, exactly as it is for folding itself. Its own decisions become marks instead of folds, and it commits the epoch on one condition: occupancy reaching `maxTarget` of the serving budget. Nothing else fires a commit automatically. Each automatic commit carries a target: it folds down toward `minTarget`, so if the agent's marks would not reach that line the law tops the epoch up with the stalest unprotected spans until they do, and the commit result records which marks were the agent's and which were the top-up. The gap between the two lines is the hysteresis, which is what makes the spacing between events structural rather than hoped for.
136
140
 
137
141
  Three refinements keep the two phases from getting in the way. A fold whose span sits within the last few messages of the window applies immediately even in epoch mode, because almost nothing follows it to invalidate. An `expand` issued while marks are pending opens the commit epoch, so a restore plus a batch of folds costs one rewrite between them rather than two; `peek` stays immediate, since it appends a tool result and never edits the prefix. And a peek result is itself a completed read, so at the next commit it folds away automatically unless the agent committed to what it saw, which keeps look, decide, discard nearly free.
138
142
 
139
- Protection is unchanged. Top-up never marks protected evidence, and a pending mark that has become protected refuses at commit with a message naming it, leaving the rest of the epoch to apply. `active_context {"action":"status"}` reports the pending marks under `automatic.scheduling` with their origin, the estimated share of the window a commit would free, and the estimated prefix tokens it would rewrite.
143
+ Protection is unchanged. Top-up never marks protected evidence, and a pending mark that has become protected refuses at commit with a message naming it, leaving the rest of the epoch to apply. `pi_fold_context {"action":"status"}` reports the pending marks under `automatic.scheduling` with their origin, the estimated share of the serving budget a commit would free, and the estimated prefix tokens it would rewrite.
140
144
 
141
145
  ## Reclaiming peek output
142
146
 
143
- A `peek` returns a fold's exact stored source, bounded by the chapter cap. That copy then sits in the window as raw evidence while the fold it came from sits beside it as a placeholder: the same bytes, held twice, and the copy is the one the ladder cannot take. Immediate mode does not classify a peek batch as a completed read, so every rung measures the pressure and finds nothing eligible. On a workload that peeks freely this is the whole failure: a measured 64-stage session ended with 14 raw peek results holding 1.9M characters, 82% of everything still unfolded, while the ladder had already folded 53 of its 56 stage results and had no supply left.
144
-
145
- `foldPeekResults: true` hands that supply back. The peek batch becomes a completed read like any other, eligible for the same cadence rung under the same protections: the freshest turns and the byte tail still stay raw, protected evidence is still refused, and the fold record is the ordinary reversible one, so the source recovers exactly. Nothing about the ladder's thresholds changes; only what counts as foldable does. Epoch scheduling already carries this classification with it, and the option is how immediate mode reaches the same place.
147
+ A `peek` returns a fold's exact stored source, bounded by the chapter cap. That copy then sits in the window as raw evidence while the fold it came from sits beside it as a placeholder: the same bytes, held twice, and the copy is the one the ladder cannot take. A runtime that does not classify a peek batch as a completed read measures the pressure at every rung and finds nothing eligible. On a workload that peeks freely this is the whole failure: a measured 64-stage session ended with 14 raw peek results holding 1.9M characters, 82% of everything still unfolded, while the ladder had already folded 53 of its 56 stage results and had no supply left.
146
148
 
147
- Reach for it when the agent uses `peek` as a browsing verb rather than a rare lookup. Leave it off when a peeked span is normally the span the agent is about to work from, since folding it back means peeking again to read it.
149
+ So a peek result is classified as a completed read, unconditionally. The peek batch is eligible for the same tool rung under the same protections: the fresh tail still stays raw, protected evidence is still refused, and the fold record is the ordinary reversible one, so the source recovers exactly. Nothing about the ladder's thresholds changes; only what counts as foldable does. The cost is that a peeked span the agent is about to work from can fold back before it is used, which means peeking again to read it; the duplicate window it otherwise leaves behind is the larger bill.
148
150
 
149
151
  ## What the invariant cost
150
152
 
@@ -152,13 +154,13 @@ Three separate features shipped that each rewrote the window mid-prefix, and eac
152
154
 
153
155
  Peek reclamation was the costliest. It removed duplicate bytes from the window after a peek and deferred the removal until a later assistant message existed, so the edit would land behind the tail. That deferral is what makes it look tail-local and what guarantees it is not: waiting for a later message is precisely the condition under which the window has already grown over the site of the edit. Measured 2026-08-07, two such reclamations cost 100k fresh tokens. A peek is now append-only, and its duplicate bytes are reclaimed by the tool-fold rung at the next commit, the one moment a rewrite is already being paid for.
154
156
 
155
- The ephemeral surfacing slate went the same way, and it deserves its own note because the idea was not the problem. It scored the fold set against the live window and offered the agent a short slate of folds worth peeking at or expanding, and the selection was useful. What failed was the delivery: the slate was shown on one pass and withdrawn on the next so a recomputed slate could take its place, which is a show-then-retract. Its bytes occupy prefix positions in one request and different bytes occupy them in the next, so the cached prefix dies. The selector and the suggestion-source interface are still in the tree and still gated off; nothing renders a suggestion in this build. Any future version has to put a suggestion in front of the agent without the window changing underneath it, and the shape that survives the invariant is the open problem in [docs/next-steps.md](https://github.com/shaneconner/pi-fold/blob/main/docs/next-steps.md).
157
+ The ephemeral surfacing slate went the same way, and it deserves its own note because the idea was not the problem. It scored the fold set against the live window and offered the agent a short slate of folds worth peeking at or expanding, and the selection was useful. What failed was the delivery: the slate was shown on one pass and withdrawn on the next so a recomputed slate could take its place, which is a show-then-retract. Its bytes occupy prefix positions in one request and different bytes occupy them in the next, so the cached prefix dies. What is deleted is the delivery, not the selection: the per-request carrier is gone and so is the external suggestion-source channel that fed it, which was a registration into a carrier that no longer exists. The selector survives, and the delivery that replaced it rides the commit boundary: at most one suggestion, carried as literal persisted bytes where the rewrite is already being paid for. A fold is suggested only when its content matches the current work while its brief does not say so, which points the agent at exactly the fold whose placeholder undersells it; a fold whose suggestions are ignored twice with no takes is silenced permanently.
156
158
 
157
159
  Anticipatory guidance went with them: pressure milestones, the live advisory, curation reminders, the last-call notice. Guidance about a coming event has to arrive before the event, so it can never ride a rewrite the runtime was already paying for; it has to create one. Eleven runs also say the warning bought nothing: voluntary fold share was 0.00, and the three runs built specifically to invite curation produced 0, 1 and 0 voluntary folds. What is left in the window is a bounded receipt of what the runtime just did, built only when a commit produced one, landing inside the freeze, never rendered twice.
158
160
 
159
161
  Earlier releases shipped truthful capacity accounting, admission control on `peek` and `expand`, retained pending marks, stage-identified fold briefs, the current-turn commit guard, the pinned-mass backstop, the status index diet and the append-only projection itself as individually opt-in levers. All of them are now simply how pi-fold works, and the flags are gone rather than deprecated; git history is the lineage.
160
162
 
161
- `active_context {"action":"status"}` reports real projection rewrites separately from observed provider-side cache misses, per-message digests included: a miss on a projection the runtime did not rewrite is provider-side by construction, and conflating the two hides exactly the bug this invariant exists to prevent. On the complete 64-stage run that instrument read 76 pure appends, 6 rewrites, and 0 unattributed rewrites, with the six rewrites landing batches of 12, 14, 9, 11, 9 and 7 folds.
163
+ `pi_fold_context {"action":"status"}` reports real projection rewrites separately from observed provider-side cache misses, per-message digests included: a miss on a projection the runtime did not rewrite is provider-side by construction, and conflating the two hides exactly the bug this invariant exists to prevent. On the complete 64-stage run that instrument read 76 pure appends, 6 rewrites, and 0 unattributed rewrites, with the six rewrites landing batches of 12, 14, 9, 11, 9 and 7 folds.
162
164
 
163
165
  ## Relation to prior work
164
166
 
@@ -172,32 +174,19 @@ The design also pairs with Pi's session trees: a fold's collapsed-or-expanded st
172
174
 
173
175
  ## Configuration
174
176
 
175
- The package entry calls `registerPiFold(pi)` with the defaults below. Hosts that call the named `registerPiFold` export directly may pass an options object. Replacement lists and sets replace the defaults; they are not additive.
177
+ The package entry calls `registerPiFold(pi)` with the defaults below, and that is the whole surface: five options, and every other name is refused with the replacement named in the message. One VALUE is refused as well, `summarizer: "deterministic"`, because the deterministic brief is the automatic fallback on any summarizer failure rather than a mode to select. Hosts that call the named `registerPiFold` export directly may pass an options object. Replacement sets replace the defaults; they are not additive.
176
178
 
177
179
  | Option | Default | Effect |
178
180
  | --- | --- | --- |
179
- | `toolName` | `"active_context"` | Name of the registered context tool. |
180
- | `toolLabel` | `"Active Context"` | Human-facing label for the registered context tool. |
181
- | `brandNoun` | `"active-context"` | Short noun used in fold placeholders, fence text, and native-compaction notices. |
182
- | `entryTypePrefix` | `"pi-fold-active-context"` | Prefix for durable state, fold-record and status identifiers. Its namespace also derives measurement/compaction entry types, status source metadata, MCP evidence ownership, the evidence directory, and the unparseable MCP-server fallback. Changing it starts a separate state namespace. |
183
- | `commandPrefix` | `""` | Prefix for `/context` and `/fold-context`; trailing hyphens are removed. |
184
- | `commandNames` | `undefined` | Optional full-name overrides for the effective defaults `{ status: "context", fold: "fold-context" }`. Both names must be distinct kebab-case strings; supplied values take precedence over `commandPrefix`. |
185
- | `summarizer` | `"session"` | Brief generator: the live session model, `"deterministic"` for no model calls, or `{ provider, model, effort? }` resolved through Pi's model registry. Cannot be combined with `summarizeContextSpan`. |
186
- | `summarizeContextSpan` | `undefined` | Optional async brief generator. Results must include a useful bounded brief, provider/model/effort attribution, and `toolCalls: 0`; failure uses the deterministic brief. |
187
- | `foldScheduling` | `"immediate"` | `"immediate"` applies every fold when it is made. `"epoch"` makes a fold a free pending mark and applies all pending marks in one projection rewrite when the runtime commits. See [epoch fold scheduling](#epoch-fold-scheduling). |
188
- | `foldPeekResults` | epoch mode `true`, immediate mode `false` | Classify a `peek` result as a completed read, so the tool-fold rung may reclaim it. See [reclaiming peek output](#reclaiming-peek-output). |
189
- | `providerTotalWindow` | the transport's descriptor | Total serving window. Every ratio, fence and budget is computed against this minus the output reservation actually in force. Supply it when the deployment knows the real ceiling: the per-request max-input descriptor a provider advertises assumes a full reservation and understates it. The descriptor is read and reported in status either way, so the gap stays auditable. |
190
- | `toolActions` | all nine actions, plus `unmark` in epoch mode | Replacement allowlist drawn from `status`, `peek`, `fold`, `expand`, `refold`, `protect`, `unprotect`, `rebrief`, `reboundary`, and `unmark`. There is deliberately no agent-callable `commit`: marking is the agent's job and folding is the runtime's. |
191
- | `blockingTools` | `["Agent"]` | Replacement list of tool names that trigger one opportunistic stale-tool fold before the call. Use `[]` to disable it. |
192
- | `readOnlyTools` | `new Set(["read", "grep", "find", "ls"])` | Replacement set of tool names whose completed batches may fold automatically. Defaults to Pi's built-in read-only tools. Pass a `ReadonlySet<string>`. |
193
- | `isMcpTool` | `() => false` | Synchronous predicate enabling evidence ingestion for oversized structured MCP results. Names shaped as `mcp__<server>__<tool>` project the actual server; other names use the neutral namespace derived from `entryTypePrefix`. |
194
- | `evidenceIngestion` | `true` | Set to `false` to register no evidence-ingestion hook and write no evidence artifacts. |
195
- | `setProjectionProvider` | `undefined` | Optional host callback that receives the [projection-candidate provider](#projection-candidate-records). Normal Pi context events do not require it. |
196
- | `setSuggestionSourceRegistrar` | `undefined` | Optional host callback that receives the suggestion-source registration function. Sources register and their accept reports persist, but nothing renders a suggestion in this build; see [what the invariant cost](#what-the-invariant-cost). |
181
+ | `thresholds` | `{ maxTarget: 0.80, minTarget: 0.35, freshTail: 0.02, consolidateAfter: 10 }` | The thermostat, set whole or not at all. The first three are proportions of the serving budget; `consolidateAfter` is a positive integer count. Validated atomically at registration, and an impossible policy is refused by name rather than clamped. User policy only: no agent action can read it back as a mutable surface, though status reports the values. |
182
+ | `summarizer` | `"session"` | Brief generator: the live session model, or `{ provider, model, effort? }` resolved through Pi's model registry. Those are the only two forms. A cheap model from the same provider is the recommendation, since a brief is a bounded factual summary that does not need the session model's reasoning budget: on a Codex session, `{ provider: "openai-codex", model: "gpt-5.6-luna", effort: "medium" }`. Every summarizer failure falls back to a deterministic brief on its own, so there is no value that selects one. |
183
+ | `providerInputBudget` | the transport's descriptor | The tokens this deployment may actually put into a request, already net of whatever output reservation it holds back. Every ratio, fence and budget divides by it directly: pi-fold subtracts nothing from a declared value. Supply it when the deployment knows its real ceiling, because the per-request max-input descriptor a provider advertises assumes a full reservation and understates it. With no value supplied the runtime falls back to that descriptor and estimates the reservation out of it, which is the conservative default for a model it has no budget fact for; the descriptor is read and reported in status either way, so the gap stays auditable. |
184
+ | `blacklistAutoFoldTools` | `new Set()` | The exception list. Every completed tool batch is foldable by the runtime on its own, without an agent mark; name a tool here and its results stay raw instead. Empty by default, because foldability is not the protection: pins, the fresh tail and the commit guard are, and they apply to a blacklisted tool and an ordinary one alike. Pass a `ReadonlySet<string>`. Blacklisting is not free: the mass stays in the window until a chapter fold takes it, and a closed unit that alone exceeds the chapter cap is reclaimed whole rather than in pieces. |
185
+ | `guidance` | `{ thresholdNotices: true, actionResponses: true }` | The two guidance surfaces, set together. `thresholdNotices` is the append-once occupancy waypoint at 25, 50 and 75 percent of the serving budget; `actionResponses` is the persistent acknowledgement a context action returns. Booleans only, and off means the carrier is absent rather than empty. |
197
186
 
198
187
  ### Projection candidate records
199
188
 
200
- The provider emits one record per visible collapsed fold. The fields and current values are:
189
+ `registerPiFold` returns `{ projectionCandidates }`, a provider a host may call with a Pi context. It emits one record per visible collapsed fold. The fields and current values are:
201
190
 
202
191
  | Field | Emitted value |
203
192
  | --- | --- |
@@ -208,7 +197,7 @@ The provider emits one record per visible collapsed fold. The fields and current
208
197
  | `horizon` | `"working"` |
209
198
  | `source_id` | Fold ID. |
210
199
  | `source_version` | Exact source SHA-256. |
211
- | `route` | `{ tool: <toolName>, arguments: { action: "expand", id: <fold-id> } }` |
200
+ | `route` | `{ tool: "pi_fold_context", arguments: { action: "expand", id: <fold-id> } }` |
212
201
  | `token_cost` | Estimated replacement tokens, rounded up with a minimum of 1. |
213
202
  | `expansion_cost` | Estimated exact-source tokens, rounded up with a minimum of 1. |
214
203
  | `rank` | Zero-based position among emitted candidates. |