pi-continual-learning
extensionmaintainedContinual learning for Pi at the harness and prompt surfaces — declarative tool-call guardrails plus memory retrieval, injection, and consolidation. Model weights are out of scope.
by — · v0.4.0 · published 2d ago
$ pi install npm:pi-continual-learningSignals
Download trend
691 downloads · last 12 weeks (weekly)
README
pi-continual-learning
Learns durable Memory and scoped Harness rules from completed user tasks. Model weights are out of scope.
- Memory supplies a bounded relevance index before generation. Full entries are read only when needed; omitted entries remain discoverable.
- Harness uses one flat
ruleslist for new declarations:skillandtextdeliver scoped guidance, whilebashsupplies messages and optionally confirms or blocks a command before execution. Installed legacy policies retain their original protections through read-only compatibility. - AGENTS.md consolidation keeps common project instructions small and moves detailed knowledge into Memory or skill rules, retaining conditional pointers when needed.
Install and commands
Python 3 must be available as python3 for the bundled Memory validator.
pi install npm:pi-continual-learning
| Command | Purpose |
|---|---|
/memory | Manage model, instructions, memory folder, auto-memory, phase policies and history |
/memory policy [memory|harness|agents] [apply|propose|off] | Inspect or set automatic learning policy for one phase |
/memory history [change-id] | Inspect private project change history or a recorded proposal |
/memory undo <change-id> [--yes] | Restore one applied change after preview and conflict checks; headless use requires --yes |
/memory evaluate <suite.json> | Compare supplied baseline/candidate rules against independent cases, without model calls |
/consolidate | Learn incrementally from the current completed Task Slice |
/consolidate full | Explicit full-corpus maintenance |
/consolidate no-context | Full Memory maintenance without task evidence; skips Harness and AGENTS.md |
/harness | Show rules and configuration diagnostics |
/harness <request> | Create or update a project-shared rule |
/harness --local <request> | Explicit personal project configuration |
/harness --global <request> | User-shared configuration |
Harness rules
{
"rules": [
{
"id": "review-checklist",
"skill": "review",
"instructions": "For this review task, check the project's documented release constraints."
},
{
"id": "project-a-status",
"text": "Project A|A项目",
"instructions": "When discussing Project A, use its confirmed replacement plan."
},
{
"id": "confirm-publish",
"bash": "\\bpnpm\\s+publish\\b",
"action": "confirm",
"message": "Confirm the package, version, and registry before publishing."
}
]
}
review above is an example registered skill name, not a bundled skill. Use an
exact skill name available in the current session. Every rule has a stable id
and exactly one selector:
| Selector | Payload | Meaning |
|---|---|---|
skill | instructions | Guidance for that expanded /skill:<name> task |
text | instructions | Guidance for subjects matching a regex in retained conversation |
bash | message, optional action | Message with the real command result, or confirm / block before execution |
Omit action to allow a Bash call and append its message to the real result.
Empty strings, null, and observe are not actions. A block wins over confirmation;
multiple confirmation reasons produce one prompt. Missing UI, rejection, and
confirmation timeout leave the call unexecuted. Regex patterns inspect command
text, not an interpreted shell execution tree; verify both intended and unrelated
cases. Tests that might cause side effects use evaluator fixtures, not live commands.
Configuration and ownership
User-owned layers, nearest last:
<agent-dir>/harness.json— user shared.<project>/.pi/harness.json— project shared and default authoring target.<project>/.pi/harness.local.json— explicit personal choice.
<agent-dir> defaults to ~/.pi/agent and honors PI_CODING_AGENT_DIR.
There is no global harness.local.json or project .pi/agent/harness*.json layer.
A personal filename is not a guarantee of Git exclusion; check ignore settings.
Package defaults are the outermost layer.
The nearest declaration of an id replaces the whole rule. Different identities
remain independent. { "id": "example", "enabled": false } disables an inherited
identity; a complete nearer definition can re-enable it. Invalid winning rules
are diagnosed rather than replaced with an outer same-id rule. An unreadable or
malformed layer is marked incomplete; any retained parse snapshot is labelled
stale, never verified as the current file.
Authoring reads the exact supplied file, assesses whether the request can be
represented, and writes a complete valid candidate once. Unsupported or ambiguous
requests leave files unchanged. Recommending AGENTS.md as a better home does not
authorize editing it. Existing invalid data requires an explicit native confirmation
preview before replacement; headless repair fails closed. If several layers need
repair, a strictly valid, approved target can be repaired independently while
unchanged other layers still have diagnostics. Activation remains incomplete and
Bash stays fail-closed where coverage is incomplete until those layers are fixed.
Approval binds the exact tool arguments and predecessor bytes across the entire
call, including any later policy confirmation. Target and other-layer raw bytes
are rechecked after the final asynchronous approval; changes require fresh
validation even when resolved diagnostics are identical. Execution policy is
resolved after asynchronous preflight and repair checks, so newly installed
protections govern the call. Recovery bypasses only incomplete coverage, never a
valid policy protecting configuration reads or writes.
The native write gate validates the whole candidate; edit is blocked for
Harness targets. This is not an OS sandbox or an interceptor for arbitrary shell writes.
Automatic learning writes only project-shared configuration. Evidence and
positive/negative evaluator cases are required. New identities cannot collide
with other layers, disabled declarations, invalid declarations, or manual rules.
learnedRules is parent-owned provenance binding each learned identity to its
current revision; a manual edit invalidates automatic ownership. Updates cannot
change the selector, re-enable a rule, or weaken its Bash action automatically.
Guidance scope and delivery
A skill read is not an invocation. Skill guidance names the applicable skill task in model-visible text. Text guidance names its matching condition and does not turn a historical mention into permission to apply it to unrelated work.
At before_agent_start, text matching scans the current prompt plus retained
branch conversation: user and assistant text, visible tool results and string
arguments, and retained summaries. Thinking, metadata, excluded shell output,
abandoned history, and Harness's own messages are not matching sources.
Guidance is a persistent tail message, not a changing system prompt. Text revisions already retained are not appended again. Delivery revisions include the message format, so reload/resume appends one scoped replacement for an older unscoped delivery. If compaction removed its original trigger, an outdated delivery is retired once instead of injecting unrelated instructions; a later matching task can reactivate it. Updates and retirements are new tail messages; previous history stays in place for traceability and stable request prefixes. Incomplete scans preserve previous delivery state. Guidance is prompt text, not a guarantee that the model follows it.
Known limits: keywords first introduced by tools in the same run are picked up at the next agent start. Synchronous regex evaluation has volume limits, not preemptive cancellation of a single pathological pattern. Avoid unbounded patterns. Configuration readback proves persistence only; report effective activation, actual trigger verification, and unverified behavior separately.
Transcript display
Expanded policy rows keep the policy, action, outcome, tool, source and file;
check rows keep their phase, policy and artifact path. Reasons already shown in
the title and check statuses already shown in its label are not repeated as
fields. Guidance rows likewise carry the [harness] tag and show the skill or rule as
their title subject, with source, file and the complete prompt below, including prompts longer than 50
lines. Clipped titles wrap when expanded. This display-only cleanup does not
change enforcement, guidance delivery or stored event data.
Existing configuration and safe upgrades
Installed policies, disabled, and skillPrompts remain supported runtime
input through a read-only compatibility boundary. A compatible old format is
reported as a notice, not unavailable configuration. In particular, a valid
write/edit-only policy does not block unrelated Bash commands after an upgrade.
Loading preserves every configuration byte. Existing cumulative disabled names
retain their historical same-ID effect, including saved flat rules, so upgrading
does not reactivate paused protections. Legacy same-name overrides of equivalent
built-in policies also keep their prior meaning.
New authoring and learning still produce flat rules. Adding them preserves
existing containers and provenance. Removing installed protections requires an
explicit before/after preview and authorization; old manual or learned entries
do not become automatically owned flat rules. No automatic migration is performed.
Legacy output/artifact checks, tool-argument paths and require gates retain
their original behavior. Legacy skill guidance retains its exact registered
invocation, optional user-message matcher, and configured system/user target.
These are compatibility capabilities, not new flat selectors. Mixed rules must
not bypass a matching block or create redundant approval prompts. Invalid
policies restrict only their identifiable tool/phase scope; unknowable scope or
unreadable configuration is conservatively diagnosed.
Built-in protection for futile interactive authentication and bulk Memory deletion
remains. A weaker Bash/text rule is never presented as an equivalent replacement
for an installed file or artifact policy. See the upgrade decision in
docs/adr/0005-preserve-installed-harness-protections.md in the source repository.
Learning pipeline
With auto-memory enabled (the default), agent_settled runs a deterministic
zero-token evidence screen after queued continuations and retries finish.
Routine tasks launch no learning model. A metadata-only selector chooses the
minimum sufficient existing Memory and relevant phases for the current Task
Slice. Selected bodies and task evidence form one authoritative Learning Dossier.
Only explicit full maintenance explores the whole corpus.
Package-owned prompts/*.md protocols are bound through typed builders. Planners
are read-only, have resource discovery disabled, and return bounded deltas or a
no-op. The parent owns identity, evidence, privacy, path and source-hash checks,
mutation, rollback, indexes, and receipts. An assistant's success claim is not a
receipt. Later planning can overlap; mutation remains sequential. A later phase
failure does not undo an earlier verified phase.
Real user tasks are coalesced while learning runs; extension continuations do not train themselves. Print/JSON sessions await completion and receipts. Turning auto-memory off stops learning without disabling Memory retrieval. Receipts count every selector/planner attempt, applied operations, duration, input/output tokens, cache reads/writes, and provider cost availability.
An explicit /consolidate always reaches selection, including tasks without
automatic-screen keywords. Explicit continuation prompts retain the original
request and its evidence. Task slices preserve complete entries within their byte
budget, report omitted entries, and reject an oversized original request rather
than clipping evidence. A failed tool invocation followed by a successful check
is eligible for Memory review; the planner still must justify a durable lesson,
and a reviewed selector verdict is not overridden by that recovery alone.
Harness evidence means Harness-owned transcript evidence: guardrail event
entries, Harness guidance delivery, and the notes Harness attaches to tool
results. Repository text that merely mentions policies or harness files is not
evidence, so it starts no planner.
Phase policies and history
All three automatic phases default to apply, preserving existing behavior.
propose validates and saves a private plan without changing learned files;
off skips that phase. For example, /memory policy harness propose leaves
automatic Memory and AGENTS.md behavior unchanged. Policies are stored in
<agent-dir>/memory/settings.json under automaticPhases, keyed by memory,
harness and agents. Explicit /consolidate and full maintenance still apply
validated current plans; the existing agentsMd.disabled setting remains
authoritative. AGENTS extraction is saved as a proposal if it would write to
a Memory or Harness phase configured to propose or off.
Applied mutations and proposals are recorded privately under
<agent-dir>/memory/history/<escaped-canonical-cwd>/. History includes exact
before/after bytes, including indexes and mirror repairs. /memory history
lists the latest 50 records; passing an id shows a bounded preview. Proposal
records are inspection artifacts; running /consolidate creates a fresh plan
against current evidence instead of replaying a stale proposal.
Undo requires all affected files to match the recorded successor, validates
project scope and Memory privacy, and runs under the consolidation lock. It
refuses later edits, symlinks and invalid records. TUI use confirms a concrete
preview; headless use requires the id and --yes. Interrupted undo is recovered
to the applied state on the next learning session, unless an unrelated edit
requires inspection. Pending records from failed mutations cannot be undone.
If an interrupted mutation leaves bytes or permissions different from its recorded
predecessors, subsequent learning stops with a history id for inspection. Existing
AGENTS.md transaction recovery runs first. Reconcile these files to their exact
predecessors after review to resume; the incomplete record becomes abandoned.
Unknown current bytes are never automatically attributed to learning or overwritten.
History rejects detected credentials in proposals and file snapshots, matching
assigned values and provider key formats rather than ordinary prose, wiki links
or workflow permission names. A refused snapshot names the learned surface to
clean without echoing the matched bytes. Completed runs keep their run directory
(identity, task, plan, receipts, snapshot) for inspection, like a failed run keeps
its diagnostics.
Older changes made before history was installed have no undo record.
Independent learning evaluation
/memory evaluate examples/learning-evaluation.json compares embedded baseline
and candidate Harness configurations against user-authored held-out cases.
The report includes accuracy, false blocks, missed protections, regressions and
configuration/suite digests. Cases support Bash decisions and skill/text matches.
No commands or models execute. These cases are independent of a planner's own
validation examples; passing them measures the configured rules, not model quality.
For an explicit paired model-task experiment, run from this package directory:
uv run --no-project scripts/evaluate-learning.py examples/learning-task-evaluation.json --validate
uv run --no-project scripts/evaluate-learning.py examples/learning-task-evaluation.json --model <provider/model> --output /tmp/learning-evaluation.json
Each task runs once per arm in a fresh disposable Git project with supplied Memory
snapshots, automatic learning disabled, and only the read tool. Authentication is
copied privately from the configured agent directory; the source is unchanged.
The report includes literal-answer success, regressions, latency and observed
token usage/cost; missing prices remain unavailable. Output files are private and
never overwritten. Without --model, the configured memory.json model is used.
The included synthetic example verifies retrieval plumbing; use representative
held-out tasks for your project. One paired sample per task does not establish
statistical improvement, and this experiment does not test Bash enforcement.
Memory
Exactly two roots participate:
- Complete canonical private root:
<agent-dir>/memory/<escaped-canonical-cwd>/. - Safe Git-trackable mirror:
<canonical Git project root>/.memory/.
The project mirror is enabled only at the canonical Git root. There is no
project-local private Memory root. Safe entries synchronize byte-identically;
private entries exist only in the private root and are marked (harness only)
in its MEMORY.md. Preferences stay private; credentials and secrets are never
stored, including in private files or evidence.
The root name uses Pi's flat path escaping: strip the leading slash, replace
separators and colons with -, and wrap with --. Names beyond 240 bytes fail
explicitly; no hash fallback exists. Paths with the same escaped name share the
root, lock and run storage. Obsolete roots are neither discovered nor migrated.
Before planning, newer-mtime-wins drift normalization synchronizes safe entries; ties prefer the private copy. First adoption can import committed shared Memory when the private root is absent. Privacy leaks and public orphans are removed under the existing parent-owned validation contract.
Retrieval and descriptions
The default injection budget is 6,000 characters. Entry bodies are not injected. Each body root is declared once and every entry row names its filename, so the budget reaches relevance metadata instead of repeating an absolute read path per entry. Descriptions should begin with when the entry is relevant and fit one line of at most 120 characters; detail belongs in the body. Existing longer descriptions are retained when they fit, or shortened with a single-line marker rather than silently losing a late trigger. Under budget pressure every listed entry stays reachable and cues give way before entries do.
The loader reports unique entry counts and file-read omissions; final formatting
reports budget omissions. Discovery pointers name MEMORY.md and exact roots
for bounded reads/listing. Indexes are discovery metadata and can be stale;
individual bounded files remain the authority. Parent rebuilds include descriptions
in complete indexes. Loading memory never writes indexes. All retrieved content
is explicitly untrusted reference data, not instructions.
Loading runs on every user turn, so it avoids repeat work without weakening its
checks: the read buffer is sized from the file rather than from the byte bound,
unchanged entry metadata is reused in-process while its device, inode, size,
mtime, and ctime still match a regular non-symlink file, the root identity is
re-verified for every real read and for the batch, and the git root probe is
memoized per canonical project path within a bounded window. A rewritten,
replaced, or symlinked entry is always read again or dropped, never served from
reuse.
Only strict [A-Za-z0-9][A-Za-z0-9_-]*.md basenames are entries; MEMORY.md is
metadata. Symlinks, non-regular files, escapes, and root replacement are rejected.
New Memory cites exact indexed user/tool-result evidence and cannot overwrite
existing names. Deletes require a contradicted, superseded, or subsumed verdict
plus a mechanically verifiable preservedIn target.
AGENTS.md consolidation
A read-only planner proposes at most five small evidence-cited operations against project-root AGENTS.md: rewrite, remove, add, or extract. Indexed quotes must match user/tool-result content; assistant text and metadata do not count. New units require two distinct evidence entries. The parent simulates exact anchors and byte budgets before applying changes.
Common constraints and short task-to-document pointers remain always loaded.
Detailed durable knowledge need not be relevant to every task: it can move to
Memory. Registered skill-specific instructions can become a flat skill rule.
An extraction can retain a bounded single-line replacementText pointer; it
participates in the same evidence, anchor, size, fingerprint and rollback checks.
A lack of use in one task is not evidence that an instruction is obsolete.
Memory, Harness, AGENTS.md, and receipts participate in one extraction transaction.
A pre-receipt records exact predecessors before mutation; startup recovers an
interrupted transaction only after verifying the canonical scope. User-level
instruction files are never automatic targets. Configure the agent-global phase
in <agent-dir>/memory/settings.json:
{ "autoMemory": true, "agentsMd": { "budgetBytes": 16384 } }
agentsMd.disabled: true skips that phase. At or above budget, changes are
zero-sum rather than mandatory expansion. Empty validated plans are successful no-ops.
Development verification
python3 -m pytest packages/continual-learning/tests/ -q
pnpm typecheck
pnpm --dir packages/continual-learning pack --dry-run
pnpm check:install
uv run --no-project packages/continual-learning/tests/live_meaningful_details.py
python3 packages/continual-learning/tests/live_smoke.py
python3 packages/continual-learning/tests/live_upgrade_smoke.py
The display smoke runs real Pi print and interactive sessions with an offline scripted provider and disposable roots, without user credentials or network requests. It verifies Ctrl+O, narrow-terminal wrapping and complete guidance readback. The live scripts are not collected by pytest.
The learning smoke reuses configured model/auth in disposable project and agent roots, requires actual learned project rules and receipts, and separately verifies scoped guidance with a harmless Bash command. It never replaces failed learning with fixtures or edits real user configuration. The separate upgrade smoke starts a real Pi process with a disposable old-format configuration, confirms harmless Bash and unrelated writes execute, and confirms protected write/batched-edit calls remain blocked while configuration bytes stay unchanged. The learning and upgrade scripts copy provider credentials into private temporary roots rather than link to writable user files.
License
MIT