slice-pi0.8.0
SLICE source-linked compaction preview for unmodified Pi 1.4.0 and 1.3.0 and 1.2.0 and 1.1.0
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
SLICE for Pi: independent extension preview v0.8.0
This extension runs on unmodified official Pi 1.4.0, 1.3.0, 1.2.0 and 1.1.0 as an ordinary Pi package. v0.8.0 retains the checkpoint schema and source-hash format used by v0.4.1, so existing preview checkpoints reopen unchanged, a v0.5.0 checkpoint reopens under v0.4.3, and checkpoints v0.8.0 writes, segments included, reopen under v0.7.5.
It is published on npm as slice-pi and on Git through the extension-preview
branch and immutable extension-v* package tags, separately from the v0.3.0
resident source build.
v0.8.0 supports manual /compact and completed-turn threshold compaction,
and with the session writer compacts earlier, at a settled turn, when one more
turn would leave the writer's handover no room. Optional background
preparation with the configured writer (A), data-only work notes from the
session model (B) and segments are all disabled by default. B carries covered
user messages in full or asks the configured writer to compact them. Sources
are validated at trust boundaries, with incremental checks between them. The
synthetic long-session scenario requires both the 50 ms request target and the
growth bound under the strict profile; the first request after each checkpoint
still pays one full check.
With every switch at its default, v0.8.0 changes two things. When a tool batch
carries the session past Pi's trigger and leaves the session writer no room
for its handover, the same writer reads the evidence in bounded chunks instead
of failing with CONTEXT_WRITER_CAPACITY. On GPT-6 Luna that took 143 and
155 s, where a handover that fits takes about 20 s, and one attempt in three
was refused. The writer's rules also state the note schema's limit of 20
source IDs per item, which Luna at Pi's default thinking level had broken often
enough to block a session. Segments are new and stay off: on the 0.8.0
provider runs they did not shorten the pause. Earlier, on 2026-11-01, the
published extension-v0.7.2 compacted two first-use sessions on Luna in 13.7
and 14.0 s, reading about 95% of the writer's input from cache. The
changelog and the validation record give
the details.
Since 0.7.0 this extension is the first adapter of the host-neutral core
package (packages/compaction-core), and the resident runtime is frozen as of
2026-10-22: no further ports or features, maintenance only, retirement by
0.10.0 (see the delta's freeze note).
v0.7.1 is the first extension built on that core. It behaves as v0.6.0 did and
adds official Pi 1.4.0 as the primary host. v0.7.2 installs from the new
repository address and lets a second installed copy stand aside; it compacts
as v0.7.1 did. v0.7.5 compacts as v0.7.4 did and is the first release on npm,
beside packages that give OpenCode and DSH the same context_history. v0.8.0
adds segments as an experiment, and its DSH package an opt-in manual writing
preview.
The project is now called SLICE, Source-Linked Indexed Compaction of
Everything. Its repository moved to youxi-huang/slice, and GitHub redirects
the old youxi-huang/pi-context-compaction address. The
/compaction-* commands, the pi-context-memory.json configuration file and
stored checkpoint identifiers keep their earlier names, so nothing needs
migrating. From v0.7.5 the package itself is named slice-pi, on npm and on
Git; Pi identifies an installation by its source, not by that name.
Install, update and remove
Official Pi 1.4.0, 1.3.0, 1.2.0 and 1.1.0 users can install the pinned release from npm:
pi install npm:slice-pi@0.8.0
Restart Pi afterwards. A pinned version stays fixed. To follow later compatible releases instead, install the package without a version:
pi install npm:slice-pi
Update only this package:
pi update --extension npm:slice-pi
Pi records one entry per npm name, so installing slice-pi with or without a
version replaces the earlier entry.
The same package is on Git, pinned by its package tag:
pi install git:github.com/youxi-huang/slice@extension-v0.8.0
or following the moving branch, which pi update --extension git:github.com/youxi-huang/slice@extension-preview updates:
pi install git:github.com/youxi-huang/slice@extension-preview
Pi records one source per package address, so installing the same address with another tag or branch replaces the recorded source.
pi update --extensions updates every installed package. A plain pi update
updates Pi itself and pi update --all updates Pi and all packages; the v0.8.0
preview supports Pi 1.4.0, 1.3.0, 1.2.0 and 1.1.0.
Before updating Pi
Check that the installed preview lists the new Pi version first; v0.8.0, v0.7.5, v0.7.4, v0.7.2 and
v0.7.1 list 1.4.0, 1.3.0, 1.2.0 and 1.1.0, and v0.6.0 lists 1.3.0, 1.2.0 and 1.1.0. On a Pi version the preview does not list, sessions it never protected are left to
Pi's own compaction, but a session it protects stays blocked: requests stop and
a failure record is written, because the preview cannot check its checkpoints
on that host. To continue such a session, reopen it with a listed Pi version
and run /compact there, or keep the file and continue in a new session.
/compaction-doctor shows which case applies.
Remove the package, whichever version is installed:
pi remove npm:slice-pi
A Git installation is removed by its address, whichever tag or branch is
installed: pi remove git:github.com/youxi-huang/slice.
An installation made from Git keeps working, and one made from the old address keeps working through GitHub's redirect; removing or updating it still names that address. To move it to npm, exit Pi and run:
pi remove git:github.com/youxi-huang/slice &&
pi install npm:slice-pi@0.8.0
For an installation from the old address, remove
git:github.com/youxi-huang/pi-context-compaction instead. Add -l to both
for a project installation, then restart Pi, reopen the session and run
/compaction-doctor. Do not continue a protected session between the two
commands. Never install two copies, from two addresses or from Git and npm:
Pi loads every copy,
reports only that their tools conflict, and numbers their commands
(/compaction-doctor:1, /compaction-doctor:2). From v0.7.2, a copy that
finds another one loaded stands aside, so only one compacts, and it says once
which address to remove; from v0.7.5 it keeps the npm copy. Two copies of v0.7.1 or earlier still compete for the
next compaction and pause the session. In testing, neither
/compaction-recover nor /compact cleared that pause; remove one address,
restart Pi and continue in a new session.
Install the package from one scope only (global by default, or project-local
with -l). A compatible older preview can reopen its own checkpoint schema;
resident and native checkpoints are not migrated.
The v0.8.0 release
also provides the extension .tgz npm serves, the OpenCode and DSH .tgz,
source archive, provenance, exact-package scenario evidence and SHA256
checksums. The package contains five reviewed files and no host runtime, and
npm and Git carry the same files. Updating or removing the extension preserves
Pi credentials, other package registrations and session files.
First use
Start a new, persistent, text-only session. After Pi starts or reloads its
extensions, the preview shows Source-linked compaction is on. Pi's footer then
shows compaction: on, the number of checkpoints on the current branch,
compaction: writing note during a compaction, or
compaction: paused, see /compaction-doctor. Then run:
/compaction-doctor
It prints a short checklist: extension loaded, host version, session support,
writer, manual /compact eligibility and whether requests are paused. It calls
no model, writes no failure record and does not change the session. After a failed
attempt, the manual /compact line says which way out applies: retry when a
new /compact replaces the failed attempt, with /compaction-recover named
first when Pi already saved the staged checkpoint; blocked only when
/compact cannot help in this session, as with an unsupported host or content,
an unreadable configuration or an unavailable writer. Because Pi's own
compaction settings are not visible to extensions, it reports Pi's side of
/compact eligibility as unknown instead of guessing; Result: ready for requests means nothing is blocking requests, not that Pi will accept
/compact yet.
/compaction-status prints the same findings as JSON. It keeps the fields of
earlier previews and, since v0.3.3, adds host, supportedHost, protection,
reason and manualCompaction; a pending attempt reads pending rather than
blocked. manualCompaction.preview is ready, retry, waiting-for-turn,
blocked or not-active, and manualCompaction.recover says whether
/compaction-recover has a saved checkpoint to confirm. lastWriter and
lastCompaction describe the current session only. Since 0.5.0, lastWriter
also reports the first refused candidate's quotation failures by class
(quotations), the kind of a repair that ran (repairKind), what a
quotation repair changed (quotationRepair) and how many writer responses
called a tool instead of writing the note (toolCallResponses), and whether
one was declined (toolCallDeclined); all but the last are counts. Since
0.5.1, each call under writerCallDetails also names the reasoning effort it
asked for (effort), the reasoning settings and output cap its provider
request carried (sent, read from the request Pi built, without its content)
and how the response ended (stopReason), and quotationRepair.droppedFrom
counts withdrawn facts by section. Since 0.5.3, a call whose note arrived inside
a schema envelope names its shape under envelope, type or schema. For the first candidate refused with
CONTEXT_NOTE_SCOPE, scope counts the cited IDs that name no entry on the
branch (missing) and those of entries without readable text (unreadable,
with their kinds under unreadableKinds), and how many of either sit in
supersedes; it never names the IDs. After v0.7.2, a source of that kind is
dropped when every fact keeps a readable source and no supersedes ID is out
of scope, and the note is checked again; scopeDropped counts the IDs dropped.
Otherwise the refusal stands. For the first candidate refused on the
note schema, schema counts its facts without sources by section (uncited)
and says whether the note fails the schema for another reason too (other);
it never names the facts. Pi maps a level the model does not declare to a
supported one, so sent can differ from effort. Since Pi 1.3.0, a model that
changes effort mid-conversation, such as GPT-6 Luna, receives a fixed
reasoning.effort of medium and the requested level in a
configuration_update input item; sent.effort then reports that item's level
and sent.fixedEffort the fixed one.
Pi decides whether there is anything to compact before calling the extension.
With default settings Pi keeps the most recent 20,000 estimated tokens
(compaction.keepRecentTokens) and answers Nothing to compact (session too small) until older history exists. That answer is not an extension failure and
leaves no failure record. The scenario runner reaches its first checkpoint after
about 31,000 estimated tokens of history.
Which sessions the preview handles
The preview distinguishes three kinds of session:
- Protected: the session has a checkpoint written by this preview, or a pending or failed attempt recorded by it. Every check below is strict, and a failure is recorded durably.
- Eligible: a persistent, text-only session on Pi 1.4.0, 1.3.0, 1.2.0 or 1.1.0 with no
checkpoint from another compactor. Manual
/compact, Pi's threshold compaction after a completed turn, and earlier settled-turn compaction with the session writer all write checked checkpoints. Overflow recovery is cancelled until/compact. The first attempt, or a cancelled overflow recovery, makes it protected. - Not protected: a session the preview has never protected and cannot check: Pi outside 1.4.0, 1.3.0, 1.2.0 and 1.1.0, an in-memory session, a native or other compactor's checkpoint, an image or other non-text content, or an unsupported context edit. The preview stands aside. It writes no failure record, does not stop requests, leaves manual and automatic compaction and cache warming to Pi, and says so once per session.
Only durable claims decide protection: a preview checkpoint in the session
file or a failure record. Content alone never releases a protected session.
A protected session that later gains an image, a checkpoint from another
compactor or an unsupported host stays blocked; continue the work in a new
supported session and keep the old file. An unreadable pi-context-memory.json stops each request
in an eligible session without recording a durable block, so fixing the file
and restarting Pi is enough; in a protected session the block is recorded.
v0.3.2 and earlier treated every session as protected: an unsupported session had its requests blocked, and a failure record was written wherever the session had a file.
Build from source
With the repository's development dependencies installed, run:
node scripts/context-memory-preview-build.mjs
Then install the built directory:
pi install ./.artifacts/context-memory-preview-package
The builder produces an extension-only directory and an npm-compatible .tgz
with five reviewed files. It bundles this project's code, keeps Pi and TypeBox
as host-provided peers, and records a separate source fingerprint. It does not
rebuild or replace Pi. preview-build.json records the package version, the
clean source commit (or null for a working tree with changes) and the
independent source fingerprint.
A local-path install loads that directory without copying it. Build and verification commands in this guide run from a checkout of the development repository with its development dependencies installed.
Supported workflow
Use Node.js 22.19 or newer and a persistent text session. Keep one Pi
writer process per session and load this package through normal CLI/TUI/SDK
extension discovery. Finish turns until Pi has older history to compact, use
/compact, inspect /compaction-status, and continue. With
arm B on, the model can record work notes with
context_note: where the work stands, decisions and their reasons, failed
approaches, files and the current plan, each fact on one line with the
original entries it rests on. A note has no field for instructions. The model
retrieves original records on the current branch with context_history.
The JSONL remains the only history store.
context_history can also list one kind of item, newest first: the
constraints, decisions, failed approaches, files, plans and state that
committed checkpoints recorded, edits read from edit and write tool calls
with their outcome, and errors from failed tool results and shell commands. A
fact restated by several checkpoints appears once; one that a later fact
supersedes is marked with what replaced it; every item names its original
entries and carries an excerpt of one. A note's supersedes names entries,
not facts, so a revision marks only the facts of its own section on the entry
it names, and only when they are no more than the revisions naming it. In any
other case, such as a constraint stated in the same message as a value that
was later revised, the item carries sourceRevisedBy instead: its source was
revised, and the excerpt says whether the item was. The list is computed from the current
branch on every call and stores nothing. It can miss what a writer left out, so
search remains the way to check.
For SDK use, select the same agent directory through PI_CODING_AGENT_DIR
before importing Pi and pass it consistently to the session/resource loader.
The extension uses public getAgentDir() for configuration and operational
state; an SDK-only alternate agent directory is outside this candidate's scope.
The default writer is the session model with its current thinking level. A
fixed writer can be explicitly configured in pi-context-memory.json inside
Pi's agent directory. Authentication stays with Pi's public model registry;
the package does not copy credentials or choose a substitute on failure.
Each fact and open question in a checkpoint is one line. The writer's schema
allows line breaks, so the extension folds them into spaces before a note is
committed, and a writer cannot open a heading of its own, such as a second
## instructions. Exact quotes are rendered as JSON strings and stay exact.
Checkpoints written by v0.4.0 keep their summaries and reopen unchanged.
Since 0.5.3 every note writer asks for the note itself rather than for an
object matching the note's schema. Before validation the extension removes the
two schema envelopes GPT-6 Luna returned around complete notes: a string type
beside exactly the seven sections, and type, properties and required with
exactly the seven sections under properties. It removes nothing else, and the
note then passes every check as before.
The session writer reads the conversation without entry IDs, so its handover
request also lists every uncovered user message in full beside its ID, whole
or not at all, within the note's hard allowance; the request drops this block
before it shortens anything else. When a candidate fails only on quotations,
for at most five facts, the writer gets one repair request that names those
facts, the entries they cite and the entries that do contain the quoted text,
with their roles. A replacement may change only its sources and quote, or
withdraw the fact; every other fact stays as written, and the whole note is
checked again. A failed quotation is a fault in the evidence, not a
retraction: a repair that withdraws an instruction whose quoted text a user
message contains refuses the note with CONTEXT_NOTE_REPAIR_LOSS, since a
misquoted rule is still a rule. Re-citing it or dropping its quote remains
possible. Since 0.5.1 the quotation repair runs without reasoning, whatever the
writer's effort: it rebinds at most five quotations, and on DeepSeek it passed
as often without reasoning as at high, at a quarter of the output.
Every fact in a note, next steps included, must cite at least one entry. When
a candidate's only fault against the schema is facts without sources, for at
most five facts, the writer gets one repair request, also without reasoning,
that names those facts beside the entries they may cite: the active requests
and other user messages in full, excerpts of the entries the rest of the note
cites, and the uncovered entries by their opening words. The answer may add
sources only, and the whole note is checked again. A fact cannot be withdrawn
here: most such facts are next steps, and dropping unfinished work to avoid
citing it would commit a note that lost it silently. noteRepair governs
this repair, the quotation repair and the size repair, and a compaction spends
at most one of them. A second failure blocks as before. The extension never
moves a quotation to another entry or supplies a source on its own: text found
only in an assistant message or a tool output is not a user ruling.
The session writer's request is the session's own system prompt, tools and
messages plus one closing message, and it sets no tool choice, just as the
session's requests do. Since 0.5.3 it also carries the session's ID, which Pi
sends with each of the session's requests: Pi's OpenAI providers key their
prompt cache on that ID, and providers that need no key ignore it. When the
transcript records the system prompt itself, as Pi 1.4.0 does, the writer sends
it where the transcript has it, mid-history included in a session begun before
1.4.0, and adds the host's prompt and tools at the head only when the transcript
records none: the session's own requests are built the same way. A provider that
caches prompt prefixes therefore serves most of the request from the cache the
session has already warmed: on DeepSeek, 95 to 97% of the writer's input over
192 compactions. The closing message tells
the model to call no tool. A tool call is never run: the first is answered as
unavailable and the same request continues once. That continuation does not
spend the compaction's one repair, so a handover makes at most three writer
calls. A second tool call, or one with noteRepair off, refuses the note with
CONTEXT_WRITER_FAILED. On DeepSeek about one writer response in twenty called
a tool.
When a tool batch carries the session past Pi's compaction trigger and into
its reserve, the full handover may not fit even with bare citation IDs. The
same session model, at the same thinking level, then reads the original
evidence in bounded chunks. The choice is made before the first writer request,
never after a failure, so a writer that fails still blocks the session. Each
chunk carries the note so far with the original IDs and offsets, and all chunks
share the frozen note budget, the checks and the one repair. The price is more
calls and no prefix cache: on GPT-6 Luna a chunked compaction took 143 and 155 s
where a handover that fits took about 20 s. A larger compaction.reserveTokens in
Pi's settings makes it rarer. Segment requests and the tail a segments
checkpoint keeps are unchanged.
Configuration is latched for the process; restart Pi after changing it. The
preview uses the writer, note-budget, repair and retained-text settings from
that file. compactAt bounds the manual checkpoint budget but does not start
automatic compaction; the resident event log and its report are not provided.
Setting enabled: false and restarting ends the package's protection.
This preview compacts at completed turns: on /compact, and when Pi's
threshold compaction runs after a turn completes. With the session writer it
also starts that compaction itself at a settled turn whose context, with one
more turn as large as Pi's default reserve or as the turn just settled, would
leave the writer's handover no room. It checks again at once after /tree,
whose new branch may already sit that close to the limit. On a 272,000-token
route Pi's own trigger leaves 16,384 tokens, less than a handover takes.
One-shot print and json runs, which exit when the turn settles, are left to
Pi's trigger. Both lines
read Pi's own count of the context, from the provider's reported usage, as do
the request guards. While input is queued or a
session is blocked, threshold compaction is left for a later turn and nothing is
recorded; a blocked session still needs an explicit /compact. /compact
may follow a turn that ended on a provider error, an abort or a stopped
request at once: that turn is kept whole after the checkpoint, and the
writer's handover follows the last completed turn. Overflow
recovery, unfinished tool rounds, in-memory sessions, attachments, context
edits other than validated recovery omissions, old checkpoints, parent-history
grants and other context-transforming or compaction extensions are outside its
verified scope. In the sessions it handles, unsupported compaction is cancelled
explicitly, without native summary fallback.
Ordinary Agent retries may append an omission of the latest failed assistant response in the current
checkpoint interval. Replacement content, user-message edits and failed
responses containing tool calls remain unsupported. Normal Agent retries do
not create a compaction failure marker. A previously blocked retry session
with sufficient completed source history can recover through explicit
/compact; the marker clears only after the new checkpoint is confirmed on
disk. Text-only context-overflow failures can use the same manual recovery,
which retains the unfinished user request. /compaction-recover still only
confirms an already staged checkpoint. Published v0.3.1 assets do not contain
the retry fix.
While a session is handled by the preview, it returns stop from Pi's public
cache-warming decision event. Background warming bypasses ordinary request
guards and can append usage records during a writer snapshot, so it is paused
without changing the user's settings. Normal provider prompt caching remains
available.
Failure and recovery
An attempt writes a pending operational marker before calling the writer. Invalid JSON, incorrect quotations, unreadable sources, unavailable writers, cancellation and changed source snapshots stop the attempt. No replacement summary is requested. Further model requests are aborted while the package remains loaded, including after new input, reload and reopening a session with a persisted pending or blocked marker.
Use manual /compact for an explicit retry. A retry only clears the block
after the returned checkpoint and its exact source prefix have been read
back from the actual session JSONL. There is no clear-failure command that
lets a prompt bypass this confirmation. /compaction-status shows the state
without calling a model.
If Pi saved the checkpoint but the commit confirmation was interrupted, use
/compaction-recover. It only confirms a current-leaf checkpoint with the exact
staged candidate hash and unchanged sources, after note and disk validation.
It cannot clear a writer failure or a checkpoint that was never staged, and a
refusal changes nothing. In v0.3.2 and earlier, running it with nothing staged,
including on a healthy session, recorded a CONTEXT_PREVIEW_COMMIT_UNCONFIRMED
block that only a successful /compact cleared.
If a blocked session ends exactly at an older successful checkpoint without a
staged attempt, stock Pi may report Already compacted before invoking the
extension. Pi then needs more new history than a paused session can add
before /compact runs again, so keep the session file and continue in a new
session.
Initialization failures before any completed source turn, unsupported session
formats and incompatible hosts require a fresh supported session after fixing
the cause. Neither recovery command forces those cases through validation.
Each failure and each blocked input produces one notice (v0.3.2 could repeat
the same notice two to four times). The first explains what happened, whether
requests are paused and what to do, with the controlled error code last.
Later inputs blocked by the same code get a short warning until a checkpoint
or recovery is confirmed. Text typed in Pi's interactive editor while paused
goes back to the editor: no request starts and nothing enters the session.
Messages queued during a failed /compact come back together under one
notice. After a refused /compact, Pi adds its own Compaction cancelled
below the notice. Inputs from RPC, print mode or other extensions, and inputs
with images, still reach the request guard, where Pi adds This operation was aborted. The notice says why, and /compact is suggested only where it can
help. Pi's overflow recovery request
is reported as CONTEXT_PREVIEW_COMPACT_REQUIRED (earlier
CONTEXT_PREVIEW_UNSUPPORTED; before v0.4.0, threshold
compaction was reported the same way). After a rollback to v0.3.2, that record reads
as invalid and stays blocked until an explicit /compact, which recovers it;
a v0.3.2 record opened by v0.3.3 recovers the same way.
Markers contain identifiers, hashes and controlled error codes, never note text, transcript content or credentials. Marker IO errors block the current process. If the very first pending marker cannot write any bytes, no durable failure record exists; that failure cannot be promised across a process restart once the storage fault has been repaired. An already durable pending marker remains blocking if a later stage or checkpoint write fails.
Readback is bounded to 64 MiB and 250,000 JSONL records per session. Larger
files are blocked explicitly; they are outside this first candidate's support.
Stored checkpoints and their sources are validated in full, and the session
file is read back from disk, at trust boundaries: session start or reload, a
branch change, a new checkpoint, /compact, /compaction-recover and after
any failure. Between them a request checks only the entries added since and
compares the file's identity and length with what Pi's appends should produce.
A failed or foreign append, a truncated file or a replaced file is therefore
found by the next request. A rewrite that keeps the same file and length, or an
in-place edit of entries Pi has already loaded, is found at the next boundary
instead; it cannot pass a checkpoint commit.
Pi owns checkpoint append and may expose it in memory before the extension confirms disk state. The package checks publication before resuming requests; it does not provide the resident build's disk-before-memory writes or leases over all host operations. Removing, disabling or filtering out the package ends its protection. Leave a blocked session with the package enabled and use the explicit retry operation.
Optional preparation experiments
v0.4.0 adds optional background preparation; the pinned v0.3.3 tag does not
contain it. To opt in, add this field alongside the ordinary settings in
pi-context-memory.json and restart Pi:
{
"previewPreparation": {
"enabled": true
}
}
The configured writer prepares once per checkpoint interval, after a completed
turn, when the context enters the band just below Pi's threshold compaction
point: 12.5% of it, between 8,192 and 32,768 tokens. The point is the window
minus Pi's effective reserveTokens for the current model, read from Pi's
settings with a compaction.modelOverrides entry first, so a lowered trigger
such as a capacity profile moves preparation with it.
Pi's defaults leave the band where it was. A run has a 120-second deadline, at
most eight provider calls and no repair. One candidate is kept in memory and is
lost on restart. Earlier experimental keys (minSourceTokens, minIntervalMs,
deadlineMs, maxCallsPerAttempt, ledger) are ignored; an invalid value
disables preparation without affecting requests or the formal compaction failure
contract.
At compaction, a complete, current candidate is committed without a writer call. When turns were added after it, the candidate is still committed without a writer call for the prefix it covers: the checkpoint records that prefix, and every later original stays in context verbatim after it. This prefix path is taken only when the cut drops nothing the note did not see, follows the previous checkpoint, makes the context smaller and leaves it within 80% of the compaction threshold. Otherwise, or with custom focus, the candidate is discarded and the writer compacts synchronously from the originals.
Every writer call, preparation and compaction alike, is recorded in a
content-free journal under context-memory-preview/calls/ in Pi's agent
directory. The start of a call is on disk before the request is sent and its
settlement follows, so a call interrupted by a crash stays visible as unsettled
rather than free. Optional preparation never sends an unrecorded call and pauses
while the journal cannot be read or written; a formal compaction proceeds and
reports the gap. /compaction-status shows preparation, the journal's calls
summary and lastCompaction; lastCompaction.stageMs ends at staging and
pauseMs is set after disk confirmation.
Checkpoints from the complete and synchronous paths cover the whole branch; prefix and notes checkpoints cover what their candidate or notes saw. Synthetic execution establishes lifecycle behavior; natural hit rates, provider quality and cache economics still require evidence.
Optional reduction, excerpt and rendering experiments
v0.6.0 adds three further opt-in switches beside previewPreparation; the
pinned v0.5.3 tag and earlier do not contain them. Each is off by default and
disabled by any invalid value without affecting requests or the compaction
failure contract:
{
"previewReduction": { "enabled": true },
"previewExcerpts": { "enabled": true },
"previewRendering": { "enabled": true, "candidates": ["lineage-once"] }
}
With previewReduction on, the request's projection replaces two kinds of
tool results with one line each — an earlier read of a file that was read
again later, and a consumed result after which a later turn completed — naming
the tool, its target, the first line of its result and the record ID, which
context_history reads back in full. The reduction is recomputed from the
records on every request and never stored: the session file is untouched, the
same records always produce the same bytes, appending turns never un-reduces
an entry, and an entry is first reduced only when little follows it on the
branch or after a long recorded idle gap, so the provider's cached prefix
stays stable. User messages, assistant text, checkpoint summaries, error
results and the sole evidence of a committed fact are never reduced, tool
calls stay paired with their results, and a failed compaction blocks exactly
as before. /compaction-status reports content-free counts under
reduction.
With previewExcerpts on, when the user's prompt names an identifier-shaped
term that only compacted or reduced entries hold, the request ending with
that prompt gains one short original excerpt at its end, labelled as
historical evidence with its record ID and role, never as an instruction. At
most two excerpts of at most 200 estimated tokens each are appended, a
deterministic secret filter withholds a matching record entirely and names
only its ID, nothing is written to the session, and every earlier message is
unchanged, so the cached prefix is untouched. /compaction-status reports
counts under excerpts.
With previewRendering on, the summary the model sees is derived from the
stored note and lineage by a pure function while the stored checkpoint bytes
never change, and the recovery guards accept and require that same
derivation, so a tampered rendering is still refused. Its candidates are
selectable one at a time: folded-failed-paths folds failed paths whose
sources precede the previous checkpoint to one line with their source IDs,
lineage-once renders guidance once and folds repeated lineage state lines
with every anchor kept, and cited-roles shows the roles of the records each
fact cites, computed from the records. /compaction-status reports the
active candidates under rendering.
context_history results now state readTokens, the estimated cost of a
full read, and mark hits whose entries are already in context; read
accepts detail: "low" for a bounded opening with the full cost stated, and
full remains the default. Synthetic regressions and scenarios establish
these mechanics; token savings and recovery on a real provider are
unmeasured, and all three switches stay off until that measurement supports
a default.
Work notes (arm B)
Arm B has its own switch, also off by default:
{
"previewWorkNotes": {
"enabled": true
}
}
With it, a one-line tool guideline asks the session model for a work note after
a decision, a failed approach or a completed subtask. With B off, a fresh session
does not offer context_note. A branch with an earlier call, saved or refused,
activates it on reopen or branch navigation so the earlier calls keep their tool
declaration. This only adds the tool: an explicit activation is preserved, and
the tool stays active after moving to a branch that does not need it, until the
next session. Previously saved notes still reach the synchronous writer as
hints with B off.
At compaction with B on, the extension merges the previous checkpoint's note
with the valid notes since, in order, without a model call. A fact that
supersedes a source drops the earlier
facts citing it, and the newest plan replaces the older one. A note that lists
gaps replaces the open questions recorded before it, and an empty list closes
them; a note without gaps keeps them. Instructions from
the previous checkpoint are carried unchanged; no note can add or drop one. The
merged note covers the branch through the last work note and is committed
through the prefix path above, so everything after it stays verbatim.
The extension then appends the user's own messages up to that point, verbatim and in order, as JSON lines after the note. No message is ever shortened. The checkpoint records their entry IDs, and every full validation renders them again from the originals, so an edited message blocks the session like an edited quote.
The writer compacts synchronously instead, with the notes as hints, when those
messages do not fit in full within 5% of the compaction threshold (at most 6,000
estimated tokens), as with a long pasted log, when a note supersedes the source of a carried instruction, when a
superseded source supports more earlier facts than the note gives in their
place, when the merge exceeds the note schema or budget, when the prefix conditions fail, when
the commit would leave half the compaction threshold or more in context (the
turn of the last note stays verbatim), or with custom focus. That writer also reads the user messages an earlier notes
checkpoint carried. details.preparation.path is notes for these
checkpoints, with noteIds naming the merged notes; when B leaves a compaction
to the writer, details.preparation.notesFallback records why.
Two limits remain. supersedes is optional and names entries, not facts: a
correction recorded without it leaves the earlier fact in the note beside the
newer one, and the merge does not guess which is current. Facts also accumulate
until a note supersedes them or the writer rewrites the note, so a long task
reaches the schema's bounds (40 facts per section, 20 gaps) and falls back to
the writer more often. Both wait for a note schema with stable fact identities.
Segments (0.8.0)
Segments have their own switch, off by default:
{
"previewSegments": {
"enabled": true
}
}
With it, the session model writes a segment note in the background after a
completed turn, through the session's own request so the provider's cache
serves it. The first segment of a checkpoint interval waits until the context
reaches half the compaction threshold and covers everything since the previous
checkpoint; each later one waits for 15% of the threshold, between 8,192 and
32,000 tokens, of new history. A segment cites only the turns it covers. One
runs at a time; the segments are kept in memory and lost on restart. A fixed
writerModel writes none.
Segments are written at the session's thinking level unless effort names
another, one of off, minimal, low, medium, high, xhigh and max:
{
"previewSegments": {
"enabled": true,
"effort": "low"
}
}
A segment only records what its own turns add, and the merge carries the rest,
so a lower level costs less where it matters least. With a level of their own,
segments stay valid when the session's level changes; without one, a changed
level discards them, since another level is another writer. An unknown level
turns segments off, and /compaction-doctor says so.
At compaction the extension merges the previous checkpoint's note with the
segments, in order, without a model call, the way B merges work notes, except
that a segment may add instructions found in its own turns. When the user
changes an earlier ruling, the segment's new instruction replaces the old one,
provided it cites the user's own message and names the old instruction's
sources in supersedes. The merged note is
committed through the prefix path, so the turns after the last segment stay
verbatim, unless they hold more than one segment range (15% of Pi's trigger,
between 8,192 and 32,000 tokens) or the result would leave half the compaction
threshold or more in context. A segment still being written is waited for at most 15 seconds when
only it would let the merge commit, and details.preparation.segmentsWaitMs
records the wait. details.preparation.path is then
segments, with segmentIds and the callIds that wrote them.
The writer compacts synchronously instead, as before, when no segment is ready
or valid, after an unfinished turn, with custom focus, when a segment would
retire a carried instruction without a ruling from the user in its place, when
the merge is ambiguous or exceeds the note schema or budget, when the turns
after the last segment hold more than one range (tail), or when the prefix
conditions fail.
details.preparation.segmentsFallback records why.
Segments are an experiment. On the 0.8.0 provider runs they did not shorten
the pause over a whole session, and in one case they made it worse: the turns
after the last segment then stayed verbatim up to half the threshold, and a very
large paste or tool output soon after took the next request past the window,
where the writer had no room to compact and the session blocked. The tail is
now bounded to one range. In a rerun on GPT-6 Luna no session blocked, but
segments served no compaction, and waiting for one still being written made
each pause up to 15 seconds longer. Leave segments off unless you are
measuring them. At a max thinking level a segment can also take longer than
its two-minute deadline. The records are in
validation.md.
Segments never block work themselves. They stop for the rest of a checkpoint interval
after eight calls, or once their calls have spent as many uncached tokens as
the threshold holds, a call with unknown usage spending all of it. They also
stop after three failures of one kind in a row, until the next checkpoint.
/compaction-status shows segments with how many are ready, how far they
reach and the share of history since the last checkpoint they cover, and
calls.segmentCalls; /compaction-doctor says when they are paused and why.
segments.results counts how this session's segments ended, without content:
linked into the chain, cancelled in flight, no longer extending the chain,
refused or failed by error code (CONTEXT_SEGMENT_DEADLINE for a run past its
deadline), and links dropped when a later turn classified the chain again, by
the reason it stopped. limited counts segments the writer kept to their own
turns: writers often restate the previous checkpoint in a segment, and a fact
that cites only earlier turns is dropped there, since the merge carries the
previous note anyway.
Verification
Focused tests run through scripts/context-memory-check.mjs. The independent
acceptance runner tests the actual built artifact against an isolated official
npm Pi installation, using synthetic credentials and a loopback provider. Install
that official Pi first:
node scripts/context-memory-preview-stock-install.mjs \
--stock-root ./.artifacts/context-memory-preview-stock --version 1.4.0
The installer pins all seven official runtime packages for Pi 1.4.0, 1.3.0 and 1.2.0,
or all eight for 1.1.0 and 1.0.4, which still require chord, to the same
release. A newer host that still depended on chord would fail the installer's
dependency review rather than receive it silently. npm verifies download
integrity; the installer checks registry provenance, exact versions and isolated
resolution, then starts the SDK with networking blocked. Unused manifest and
lockfile SHA-256 report fields are omitted.
Installing only pi-coding-agent at 1.2.0 or earlier lets its caret
dependencies select a newer Pi cohort and can fail at SDK startup. No official package source is patched.
An existing coherent installation skips npm; --check verifies it without
installing. The JSON report lists each runtime package's version and integrity.
Install each older supported host for the same-package and cross-version checks:
for host in 1.3.0 1.2.0 1.1.0; do
node scripts/context-memory-preview-stock-install.mjs \
--stock-root ./.artifacts/context-memory-preview-stock-$host --version $host
done
Then run acceptance on every host. The first command also writes a checkpoint, compacts it on each older host in turn and reopens it on 1.4.0 after each:
node scripts/context-memory-preview-acceptance.mjs \
--package-dir ./.artifacts/context-memory-preview-package \
--stock-root ./.artifacts/context-memory-preview-stock \
--compatible-stock-root ./.artifacts/context-memory-preview-stock-1.3.0 \
--compatible-stock-root ./.artifacts/context-memory-preview-stock-1.2.0 \
--compatible-stock-root ./.artifacts/context-memory-preview-stock-1.1.0 \
--output-dir ./.artifacts/context-memory-preview-acceptance
for host in 1.3.0 1.2.0 1.1.0; do
node scripts/context-memory-preview-acceptance.mjs \
--package-dir ./.artifacts/context-memory-preview-package \
--stock-root ./.artifacts/context-memory-preview-stock-$host \
--output-dir ./.artifacts/context-memory-preview-acceptance-$host
done
CI and publication take this host list from the candidate's own
preview-build.json, printed by
node scripts/context-memory-preview-release-checks.mjs --plan ./.artifacts/context-memory-preview-package,
and node scripts/context-memory-ops.mjs preview runs the same sequence. Adding
a host to compatibility.ts therefore adds it to every check, and a host
without a reviewed installation cohort stops the plan.
This is deterministic protocol and lifecycle verification. It does not measure real-provider compatibility, semantic recovery quality, compaction latency or Windows behavior. No official Pi source, real account, existing user settings or session is modified by these isolated checks.
Acceptance copies the package to a temporary directory outside
any Node project and runs every case there, keeping only report.json and
per-case results in --output-dir. Inside this repository, once
npm run build:offline has run, a package's Pi imports resolve to the
repository's patched workspace build instead of the host under test; acceptance
runs before v0.3.3 were exposed to that. The runner stops if
its temporary directory sits inside a Node project.
The scenario runner checks the same artifact under default Pi and extension settings, each case beside a control run without the extension. The unsupported 1.0.4 installation only serves the other-version case:
node scripts/context-memory-preview-stock-install.mjs \
--stock-root ./.artifacts/context-memory-preview-stock-1.0.4 --version 1.0.4
node scripts/context-memory-preview-scenarios.mjs \
--package-dir ./.artifacts/context-memory-preview-package \
--stock-root ./.artifacts/context-memory-preview-stock \
--other-stock-root ./.artifacts/context-memory-preview-stock-1.0.4 \
--output-dir ./.artifacts/context-memory-preview-scenarios
Every scenario check states intended behavior. Checks in the script's
KNOWN_GAPS list describe behavior the preview does not provide yet; they are
reported without failing the run. A listed check that starts passing fails the
run until it is removed from the list, so each fix becomes a required
regression. The runner also loads a copy of the package from a temporary
directory outside the repository. The long-session case uses 20 checkpoints by
default; --perf-checkpoints 60 runs the full measurement, which takes several
minutes.
Maintainer publication
For each full release, update the extension package version and pinned install commands with the source version, then verify the matching v<version> and extension-v<version> tags and all release assets together; a source-only tag is not a completed extension release.
Merge the reviewed source through a PR and confirm verify and all three
CodeQL checks on the final main commit. Push a new immutable v<version>
source tag to that commit. The Publish extension preview workflow verifies
those checks, rebuilds and tests the package on official Pi, creates the matching
extension-v<version> package tag, updates extension-preview without a force
push, and publishes a pre-release after all assets are uploaded. Every release
since v0.4.0 also reruns the complete strict scenario suite and binds its content-free evidence
to the final artifact; the earlier A-preview exception applies only to
v0.4.0-preview.1. Existing public
assets and tags are never overwritten. The resident runtime keeps release
version v0.3.0; v0.4.1 changes its build fingerprint for the one-line note fold,
with the frozen evaluator pinned to that source.
A source tag whose version differs from the extension package it builds is a
source release, as v0.7.0 was for the host-neutral core. When that package
version is already published, the workflow ends after the build with a notice
and publishes nothing; when it is not, the run fails before any official Pi is
installed. An extension release therefore sets its new version in
scripts/context-memory-version.mjs before the tag is pushed: a
forgotten bump is reported as a source release, not as a failure. Every
package a release builds reads its version there.
From v0.7.5 the same run publishes on npm. Before anything is published it
builds slice-opencode and slice-dsh from the tag and installs each by name
on official OpenCode and DSH, as the host adapter guide
describes. After the extension's Git refs it tags opencode-v<version> and
dsh-v<version>, publishes slice-pi, slice-opencode and slice-dsh with
npm provenance, and only then makes the release public. npm never accepts a
version twice: a retry skips a package npm already holds with the same bytes,
and different bytes stop the run.
The workflow authenticates through npm's trusted publishing with the run's own
OIDC token. slice-pi, slice-opencode and slice-dsh have had a trusted
publisher since v0.7.5 (GitHub Actions, repository youxi-huang/slice, workflow
context-memory-preview-release.yml), so a release needs no token or secret.
npm sets up trusted publishing only for a package that already exists. A new package's first publication therefore needs a token, as v0.7.5 did:
- Create a granular access token that can publish new packages without an
interactive two-factor prompt, for example with
npm token create --name <name> --packages-all --packages-and-scopes-permission read-write --bypass-2fa --expires 7, and store it as the repository secretNPM_TOKEN. - Publish the release as usual.
- Add the trusted publisher, for example with
npm trust github <package> --repository youxi-huang/slice --file context-memory-preview-release.yml --allow-publish. - Delete the
NPM_TOKENsecret and revoke the token.
npm 11.21.1 warns that such tokens stop publishing directly in January 2027. A package added after that needs another route to its first version.
If publication tooling needs a fix after a source tag was created, merge the
fix through the normal PR checks. Retry Publish extension preview from that
main commit using its source_tag workflow input, or push that commit to
release/publish-v<version>. The job uses the reviewed tooling while rebuilding
and testing the immutable source tag; provenance records both commits. No tag
or existing public asset is replaced, and uploaded assets are not downloaded
again for verification. A run that fails after uploading leaves a draft
release. The retry keeps the draft's archives, which must match its rebuild
byte for byte, and replaces only the evidence the failed run wrote about
itself: release-provenance.json, preview-acceptance-profile.json and
SHA256SUMS.