@astrosheep/pi-context 0.25.1 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -7
- package/dist/build-info.json +2 -2
- package/dist/extension.js +616 -370
- package/dist/src/context/boot.d.ts +24 -0
- package/dist/src/context/boot.js +33 -24
- package/dist/src/context/budget.d.ts +9 -0
- package/dist/src/context/budget.js +19 -15
- package/dist/src/context/context-window.d.ts +41 -0
- package/dist/src/context/context-window.js +16 -1
- package/dist/src/context/prompts.d.ts +20 -0
- package/dist/src/context/prompts.js +1 -1
- package/dist/src/context/reset-artifacts.d.ts +26 -0
- package/dist/src/context/reset-artifacts.js +18 -17
- package/dist/src/context/reset-lifecycle.d.ts +89 -0
- package/dist/src/context/reset-lifecycle.js +103 -75
- package/dist/src/context/runtime.d.ts +3 -0
- package/dist/src/context/runtime.js +53 -21
- package/dist/src/context/thresholds.d.ts +33 -0
- package/dist/src/context/thresholds.js +1 -1
- package/dist/src/dream/cli.d.ts +10 -0
- package/dist/src/dream/cli.js +1 -1
- package/dist/src/dream/doctor.d.ts +2 -0
- package/dist/src/dream/doctor.js +6 -2
- package/dist/src/dream/gates.d.ts +10 -0
- package/dist/src/dream/git.d.ts +21 -0
- package/dist/src/dream/lock.d.ts +31 -0
- package/dist/src/dream/runner.d.ts +30 -0
- package/dist/src/dream/settings.d.ts +16 -0
- package/dist/src/history/history-tools.d.ts +2 -0
- package/dist/src/history/history.d.ts +57 -0
- package/dist/src/index.d.ts +39 -0
- package/dist/src/index.js +4 -4
- package/dist/src/notes/address.d.ts +26 -0
- package/dist/src/notes/address.js +8 -14
- package/dist/src/notes/constants.d.ts +3 -0
- package/dist/src/notes/constants.js +3 -0
- package/dist/src/notes/context.d.ts +10 -0
- package/dist/src/notes/context.js +33 -0
- package/dist/src/notes/frontmatter.d.ts +46 -0
- package/dist/src/notes/frontmatter.js +10 -5
- package/dist/src/notes/index.d.ts +4 -0
- package/dist/src/notes/index.js +2 -0
- package/dist/src/notes/paths.d.ts +21 -0
- package/dist/src/notes/paths.js +72 -76
- package/dist/src/notes/store.d.ts +94 -0
- package/dist/src/notes/store.js +298 -242
- package/dist/src/pi/notes/adapter.d.ts +12 -0
- package/dist/src/pi/notes/adapter.js +39 -0
- package/dist/src/pi/notes/session-replay.d.ts +16 -0
- package/dist/src/{notes → pi/notes}/session-replay.js +2 -2
- package/dist/src/pi/notes/snapshot.d.ts +33 -0
- package/dist/src/{notes/notes-snapshot.js → pi/notes/snapshot.js} +11 -3
- package/dist/src/pi/notes/tools.d.ts +2 -0
- package/dist/src/{notes → pi/notes}/tools.js +24 -21
- package/dist/src/protocol.d.ts +41 -0
- package/dist/src/protocol.js +4 -6
- package/dist/src/session-reader.d.ts +5 -0
- package/dist/src/settings.d.ts +6 -0
- package/dist/src/tool-output.d.ts +101 -0
- package/dist/src/tool-schema.d.ts +17 -0
- package/dist/test/agent-loop.test.d.ts +1 -0
- package/dist/test/agent-loop.test.js +318 -19
- package/dist/test/boot.integration.test.d.ts +1 -0
- package/dist/test/boot.integration.test.js +55 -29
- package/dist/test/budget-settings.integration.test.d.ts +1 -0
- package/dist/test/budget-settings.integration.test.js +8 -7
- package/dist/test/doctor.test.d.ts +1 -0
- package/dist/test/doctor.test.js +10 -2
- package/dist/test/dream-skill.test.d.ts +1 -0
- package/dist/test/dream-skill.test.js +69 -0
- package/dist/test/dream.test.d.ts +1 -0
- package/dist/test/helpers/extension.d.ts +115 -0
- package/dist/test/helpers/extension.js +6 -6
- package/dist/test/helpers/notes.d.ts +6 -0
- package/dist/test/helpers/notes.js +13 -0
- package/dist/test/history.integration.test.d.ts +1 -0
- package/dist/test/notes-library.test.d.ts +1 -0
- package/dist/test/notes-library.test.js +111 -0
- package/dist/test/notes.integration.test.d.ts +1 -0
- package/dist/test/notes.integration.test.js +22 -24
- package/dist/test/notes.test.d.ts +1 -0
- package/dist/test/notes.test.js +137 -7
- package/dist/test/reset-lifecycle.test.d.ts +1 -0
- package/dist/test/reset-lifecycle.test.js +142 -85
- package/docs/architecture.md +8 -8
- package/docs/reset-lifecycle.md +63 -79
- package/package.json +35 -2
- package/playbook.md +33 -32
- package/skills/dream/SKILL.md +12 -0
- package/src/context/boot.ts +44 -25
- package/src/context/budget.ts +25 -17
- package/src/context/context-window.ts +16 -1
- package/src/context/prompts.ts +2 -2
- package/src/context/reset-artifacts.ts +26 -24
- package/src/context/reset-lifecycle.ts +117 -111
- package/src/context/runtime.ts +50 -22
- package/src/context/thresholds.ts +1 -1
- package/src/dream/cli.ts +1 -1
- package/src/dream/doctor.ts +5 -2
- package/src/index.ts +4 -4
- package/src/notes/address.ts +9 -15
- package/src/notes/constants.ts +3 -0
- package/src/notes/context.ts +40 -0
- package/src/notes/frontmatter.ts +18 -12
- package/src/notes/index.ts +22 -0
- package/src/notes/paths.ts +64 -78
- package/src/notes/store.ts +308 -244
- package/src/pi/notes/adapter.ts +44 -0
- package/src/{notes → pi/notes}/session-replay.ts +3 -3
- package/src/{notes/notes-snapshot.ts → pi/notes/snapshot.ts} +13 -4
- package/src/{notes → pi/notes}/tools.ts +25 -23
- package/src/protocol.ts +5 -6
package/README.md
CHANGED
|
@@ -16,9 +16,9 @@ pi -e npm:@astrosheep/pi-context
|
|
|
16
16
|
|
|
17
17
|
## What you get
|
|
18
18
|
|
|
19
|
-
- **`wipe_memory`** — the model can request a fresh context window.
|
|
19
|
+
- **`wipe_memory`** — the model can request a fresh context window after completing a tool batch. Manual `/wipe-memory` and the budget warning instead start a close-out turn: the agent can write notes and use tools over multiple turns, then `wipe_memory` commits the reset or a successful normal stop falls back to one. Abort/error never counts as completion. Raw conversation remains in the session and history_* tools, but is excluded from the next provider context.
|
|
20
20
|
- **A boot block at every window head** — static once-per-window content (cache-stable) carrying the window identity, the recent-notes index, and a short protocol that teaches the model how to recover: notes for its own bookkeeping, history tools for everything before the reset. The five note homes are read once into that boot's snapshot; a home that is unavailable is omitted without blocking the window, and the boot says that `notes_list` can retry after recovery.
|
|
21
|
-
- **
|
|
21
|
+
- **Budget close-out** — one early reminder at the configured margin, followed (when automatic compaction is enabled) by a shared hidden warning above Pi's hard reserve. That warning arms the same multi-turn close-out as `/wipe-memory`; the hard reserve remains a separate safety reset.
|
|
22
22
|
- **`get_context_remaining`** — the live, reserve-adjusted estimate of the context budget left before Pi's compaction reserve.
|
|
23
23
|
- **Nine history/notes tools** — Codex's History/Notes actions flattened into Pi's single tool namespace; notes are real markdown files under `~/.agents/notes` (`human/`, `project/`, `agents/`, `models/`, `pi/session/`):
|
|
24
24
|
|
|
@@ -40,9 +40,9 @@ The tool descriptions the model sees are the behavioral documentation: note resu
|
|
|
40
40
|
|
|
41
41
|
## Context-window protocol
|
|
42
42
|
|
|
43
|
-
Each reset appends
|
|
43
|
+
Each committed reset first appends a native empty-summary compaction checkpoint with `firstKeptEntryId` set to the checkpoint itself (retain none), then a `pi-context/reset-marker`, hidden `pi-context/boot`, and hidden continuation. The checkpoint lets Pi's persisted canonical projection discard earlier conversation while retaining the system/tool state and empty summary wrapper. The raw session branch and history tools still retain the full conversation. The marker's `windowId` is the durable window identity; legacy marker-only branches remain readable through a narrow compatibility projection.
|
|
44
44
|
|
|
45
|
-
`/wipe-memory`
|
|
45
|
+
`/wipe-memory` waits for idle, records the shared hidden close-out warning, and triggers an ordinary model turn. The agent may write notes and use tools over multiple turns; an explicit `wipe_memory` commits after the complete batch, or a successful normal stop commits after queued messages drain. Abort/error does not commit. The budget warning arms this same close-out path; ordinary reminder guidance precedes it, and the hard reserve is a separate safety boundary. Direct `wipe_memory` remains valid without a prior warning. While pi-context is enabled, `/compact` is cancelled with an actionable `/wipe-memory` notice. Close-out requests wait for queued steering/follow-up work in the current window, while explicit tool resets commit after their batch and Pi delivers queued work exactly once. An aborted turn does not manufacture a continuation.
|
|
46
46
|
|
|
47
47
|
History remains available after reset, including earlier windows and raw JSONL. Branch navigation also remains available. If either the source or destination branch contains a reset marker, generated `/tree` summaries are suppressed with a notice because Pi's raw summary generator bypasses the context projection and could reintroduce erased history. Navigation itself is not suppressed; branches without markers retain native summaries.
|
|
48
48
|
|
|
@@ -57,7 +57,7 @@ The reminder threshold is Pi's compaction reserve plus a margin, configured unde
|
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
`reminder = reserveTokens + reminderMarginTokens`; with the defaults the early
|
|
60
|
+
`reminder = reserveTokens + reminderMarginTokens`; with the defaults the early guidance fires 24,576 tokens above Pi's reset line. When automatic compaction is enabled, the shared close-out warning starts at `reserveTokens + 12,288`; the hard reserve is the final safety boundary.
|
|
61
61
|
|
|
62
62
|
The dreamer model is configured under the same key. `--dreamer <model pattern>` on the `dream` CLI wins; otherwise a non-empty `pi-context.dreamer` string from settings applies; otherwise the automatic model is used. An invalid value (empty or not a string) is ignored with one warning.
|
|
63
63
|
|
|
@@ -67,6 +67,77 @@ The dreamer model is configured under the same key. `--dreamer <model pattern>`
|
|
|
67
67
|
}
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
## Standalone notes library
|
|
71
|
+
|
|
72
|
+
The same package provides a **Node.js TypeScript library independent of Pi**:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
npm install @astrosheep/pi-context
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { createNotesStore, type NotesContext } from "@astrosheep/pi-context/notes";
|
|
80
|
+
|
|
81
|
+
const context: NotesContext = {
|
|
82
|
+
home: "/path/to/notes", // explicit filesystem root
|
|
83
|
+
sessionId: "my-session", // safe single directory component
|
|
84
|
+
projectKey: "my-project-a1b2c3d4",
|
|
85
|
+
agent: "my-agent", // canonical lowercase slug
|
|
86
|
+
model: "my-model", // canonical lowercase slug
|
|
87
|
+
};
|
|
88
|
+
const notes = createNotesStore(context);
|
|
89
|
+
await notes.write("@project/decisions.md", "Use a shared notes library.", { origin: "user" });
|
|
90
|
+
await notes.edit("@project/decisions.md", [
|
|
91
|
+
{ oldText: "shared", newText: "host-independent" },
|
|
92
|
+
]);
|
|
93
|
+
const note = await notes.read("@project/decisions.md"); // full text/body/metadata, or undefined
|
|
94
|
+
const files = await notes.list({ pattern: "@project/**" });
|
|
95
|
+
const matches = await notes.search(["library"]);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`/notes` ships JavaScript and TypeScript declarations. It does not import Pi or read `PI_*` environment variables. Pi packages are optional peers: a notes-only installation does not install them. Using the plugin, root SDK entry, or `dream` CLI still requires Pi. This is a filesystem library for Node, not a browser storage API.
|
|
99
|
+
|
|
100
|
+
### API and identity
|
|
101
|
+
|
|
102
|
+
`createNotesStore(context)` snapshots the five required identity fields; changing the supplied object afterward does not retarget the store. It resolves `home` once, validates identity components, and creates no files until an operation needs to write. Create a new store to change identity. Multiple stores can use independent roots and identities without changing process environment.
|
|
103
|
+
|
|
104
|
+
- `write(address, content, { origin?, stale? }?)` returns `Promise<{ meta }>`. Default origin is `self`; overwriting preserves creation time, existing project ownership, and unknown metadata, and revives stale notes unless `stale: true` is supplied.
|
|
105
|
+
- `read(address)` returns `Promise<{ meta, body, text, resolvedScope } | undefined>`. **Reads update** `lastAccessed` and `accessCount` on disk; `text` includes frontmatter.
|
|
106
|
+
- `edit(address, edits?, { origin?, stale?, replaceAll? }?)` returns `Promise<{ meta, applied, resolvedScope, change }>`. Edits affect the body; metadata-only changes need no edits. Each replacement uses the evolving body in array order; the complete batch is written atomically only after every edit succeeds. `change` is a typed `{ kind, before, after }`: `kind` is `body`, `metadata`, or `file` to identify the diff inputs, or `none` with empty strings when neither body nor origin/stale changed. It is not a rendered diff.
|
|
107
|
+
- `list({ pattern?, scope?, who? }?)` returns `Promise<NoteRow[]>`, sorted by update time descending with address tie-breaking. Rows contain address, scope, virtual path, metadata, body, and body byte size. `scope` narrows the five-home view; `who` names a concrete agent/model home.
|
|
108
|
+
- `search(queries: string[], { pattern?, scope?, who? }?)` returns `Promise<NoteSearchRow[]>`, sorted by address. Matching is case-sensitive literal OR over body lines; matches contain one-based `line`, `text`, and `offsetChars` into the serialized read text. Neither listing nor search increments access metadata.
|
|
109
|
+
|
|
110
|
+
`list` and `search` share the `NotesQuery` type. A merged query uses `{ pattern? }`; a single-home query adds `scope`. Only `scope: "agent" | "model"` accepts `who`. TypeScript rejects combinations such as `{ scope: "project", who: "root" }`, and JavaScript callers receive a runtime refusal.
|
|
111
|
+
|
|
112
|
+
The library returns full data, not tool envelopes or paginated/truncated output. `NoteError` exposes the existing named store refusals through `code`, with `lineNumbers` for ambiguous edits and `editIndex` for a failed edit. Runtime API fields and known persisted note metadata use camelCase; Pi tool wire fields such as `updated_at`, `offset_chars`, and `replace_all` retain their established names. A note carrying a known legacy snake_case metadata key is refused with an explicit manual-migration-required error; import and construction never migrate note data. Invalid addresses/identities and filesystem failures reject; only a missing `read` returns `undefined`. Notes remain markdown files with the existing size limits and same-directory atomic rename. Same-file read/modify/write work is serialized by absolute physical filename across all store instances in this process (including `.md` address aliases); symlink/case aliases and cross-process locking are not guaranteed. `list` and `search` asynchronously traverse homes and serialize each discovered file read against pending mutations, but are not global snapshots and may not discover a file created after traversal. Foreign named homes can be read (including the access-metadata update), but their bodies cannot be written or edited through the store. These are cooperative address rules, not an OS security sandbox.
|
|
113
|
+
|
|
114
|
+
Addresses use bare paths, `@project/`, `@human/`, `@self/`, `@model/`, or explicit `@agents/<slug>/` and `@models/<slug>/`. Relative self/model addresses resolve to the supplied identity; listing renders their concrete names. The disk layout remains `pi/session/<sessionId>/`, `project/<projectKey>/`, `human/`, `agents/<agent>/`, and `models/<model>/`. No data migration happens on library import or construction. Notes already using camelCase metadata retain their metadata; known legacy snake_case keys require the root-coordinated manual migration before use. New session notes record the supplied project key.
|
|
115
|
+
|
|
116
|
+
### Library and plugin boundary
|
|
117
|
+
|
|
118
|
+
Ownership is explicit in the file tree:
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
src/
|
|
122
|
+
notes/ # host-independent library
|
|
123
|
+
index.ts # deliberate public exports
|
|
124
|
+
context.ts # explicit identity validation and snapshot
|
|
125
|
+
store.ts # five storage operations
|
|
126
|
+
address.ts # address parsing and matching
|
|
127
|
+
paths.ts # disk layout and project identity
|
|
128
|
+
frontmatter.ts # persisted metadata codec
|
|
129
|
+
constants.ts # storage limits
|
|
130
|
+
pi/notes/ # Pi integration, not part of /notes
|
|
131
|
+
adapter.ts # live identity, root defaults, activation migration
|
|
132
|
+
tools.ts # schemas, diff rendering, output budgets
|
|
133
|
+
snapshot.ts # boot's five-home snapshot
|
|
134
|
+
session-replay.ts # historical Pi session operations
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The public runtime exports are `createNotesStore`, `NoteError`, `projectKey(cwd)`, and `slugify(value)`, alongside the API's TypeScript types. The factory and `projectKey` remain synchronous; `projectKey` provides the existing repository/worktree identity algorithm, while `slugify` normalizes an agent/model name. The five store methods return promises and use asynchronous filesystem operations. Path/glob helpers, serialization, validation internals and constants are implementation details, not exported through `/notes`.
|
|
138
|
+
|
|
139
|
+
The Pi adapter supplies the root and live session/project/agent/model identity on each call. Tools and boot use the same storage implementation. Tool schemas, Pi-style edit diff rendering, wire budgets, pagination, boot selection, legacy activation migration, and session replay stay outside the library. There are no parallel legacy store/path adapters. Internal source paths are not the supported library API.
|
|
140
|
+
|
|
70
141
|
## SDK integration
|
|
71
142
|
|
|
72
143
|
SDK hosts that create a session directly can bind pi-context to the exact same public `SettingsManager` authority as the session:
|
|
@@ -77,8 +148,7 @@ import {
|
|
|
77
148
|
DefaultResourceLoader,
|
|
78
149
|
SettingsManager,
|
|
79
150
|
} from "@earendil-works/pi-coding-agent";
|
|
80
|
-
|
|
81
|
-
import { createPiContext } from "@astrosheep/pi-context/dist/src/index.js";
|
|
151
|
+
import { createPiContext } from "@astrosheep/pi-context";
|
|
82
152
|
|
|
83
153
|
const cwd = process.cwd();
|
|
84
154
|
const agentDir = "/tmp/my-pi-agent";
|
|
@@ -102,6 +172,8 @@ const { session } = await createAgentSession({
|
|
|
102
172
|
});
|
|
103
173
|
```
|
|
104
174
|
|
|
175
|
+
The previous `@astrosheep/pi-context/dist/src/index.js` SDK import remains supported. The root entry is Pi-dependent; notes-only consumers should import `/notes` instead.
|
|
176
|
+
|
|
105
177
|
The manager must be shared by the resource loader's factory and `createAgentSession`. If the host replaces its settings authority, it must create and bind a new `createPiContext({ settingsManager })` factory together with the replacement manager; an existing factory remains bound to the manager it was created with.
|
|
106
178
|
|
|
107
179
|
The default extension export is file-backed: it reads Pi's standard global settings directory plus the trusted project's `.pi/settings.json`, with project values winning per key. It cannot discover an arbitrary SDK session manager from `cwd`, environment variables, session IDs, or private SDK fields. For an injected manager, compaction settings come from the manager's public `getCompactionSettings(model)` getter, including the active model's `modelOverrides`; pi-context margins are read from the public `getGlobalSettings()` and `getProjectSettings()` scopes. Opaque runtime overrides that those public scope getters do not expose are intentionally not treated as pi-context configuration. Live public manager changes apply on the next policy query/turn, and the extension does not drain the manager's settings I/O diagnostics.
|
|
@@ -123,6 +195,14 @@ rm "${PI_NOTES_HOME:-$HOME/.agents/notes}/.dream.lock"
|
|
|
123
195
|
|
|
124
196
|
Removing a lock while a holder is running is outside the supported cooperative protocol and can let two dreams run at once.
|
|
125
197
|
|
|
198
|
+
## Dream skill
|
|
199
|
+
|
|
200
|
+
The package also provides `/skill:dream` for reviewing notes in the current agent session. The skill reads the same `playbook.md` used by the `dream` CLI; there is only one set of dream instructions.
|
|
201
|
+
|
|
202
|
+
Scope comes from the invocation directory: inside the notes store, dream may inspect across homes; in a project, it extracts from that project's material and all session notes with matching project metadata, and organizes only that project's notes. New session notes record their project key in the frontmatter `project` field. Standard linked Git worktrees resolve to the main checkout's repository root, sharing its project key and `@project/` home. Older metadata and homes remain untouched by the code: there is no automatic migration or backfill; existing data can be migrated manually. Notes without project metadata are skipped in project-only discovery. Changing directories later does not broaden the scope.
|
|
203
|
+
|
|
204
|
+
The skill stays in the current agent session. Its wrapper and shared playbook describe the dreamer's task, not runtime setup: the execution entry point is responsible for supplying readable/writable scope and establishing write permission, including locking and Git audit safeguards. The CLI supplies its own safeguards; the bare skill does not install them. Without an established scope and write permission, the dreamer asks rather than improvising runtime setup. Session records and unapproved homes stay untouched.
|
|
205
|
+
|
|
126
206
|
## Documentation
|
|
127
207
|
|
|
128
208
|
Implementation architecture and the reset lifecycle live in [docs/](docs/).
|
|
@@ -132,6 +212,7 @@ Implementation architecture and the reset lifecycle live in [docs/](docs/).
|
|
|
132
212
|
```sh
|
|
133
213
|
npm test # build from a clean dist, then run the suite
|
|
134
214
|
npm run typecheck
|
|
215
|
+
npm run test:notes-package # pack, install without Pi, typecheck and run a consumer
|
|
135
216
|
```
|
|
136
217
|
|
|
137
218
|
The harness runs against the real installed Pi `SessionManager`/`SettingsManager` in temporary directories with fake credentials — no model or network calls, and the real `~/.pi` is never touched.
|
package/dist/build-info.json
CHANGED