@astrosheep/pi-context 0.24.0 → 0.25.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 +52 -5
- package/dist/build-info.json +4 -0
- package/dist/extension.js +1861 -0
- package/dist/src/context/budget.js +150 -0
- package/dist/src/context/context-window.js +97 -0
- package/dist/src/context/prompts.js +94 -0
- package/dist/src/context/reset-lifecycle.js +134 -0
- package/dist/src/context/runtime.js +236 -0
- package/dist/src/context/thresholds.js +62 -0
- package/dist/src/dream/cli.js +1 -1
- package/dist/src/dream/doctor.js +34 -6
- package/dist/src/dream/runner.js +1 -1
- package/dist/src/dream/settings.js +30 -0
- package/dist/src/{history-tools.js → history/history-tools.js} +3 -3
- package/dist/src/{history.js → history/history.js} +8 -46
- package/dist/src/index.js +27 -94
- package/dist/src/notes/address.js +97 -16
- package/dist/src/notes/frontmatter.js +18 -3
- package/dist/src/notes/notes-snapshot.js +30 -0
- package/dist/src/notes/paths.js +64 -7
- package/dist/src/notes/session-replay.js +41 -0
- package/dist/src/notes/store.js +76 -22
- package/dist/src/notes/tools.js +7 -7
- package/dist/src/protocol.js +7 -5
- package/dist/src/settings.js +16 -0
- package/dist/src/tool-schema.js +1 -1
- package/dist/test/agent-loop.test.js +813 -221
- package/dist/test/boot.integration.test.js +167 -0
- package/dist/test/budget-settings.integration.test.js +126 -0
- package/dist/test/doctor.test.js +14 -36
- package/dist/test/dream.test.js +37 -380
- package/dist/test/helpers/extension.js +393 -0
- package/dist/test/history.integration.test.js +316 -0
- package/dist/test/notes.integration.test.js +273 -0
- package/dist/test/notes.test.js +40 -359
- package/dist/test/reset-lifecycle.test.js +248 -180
- package/docs/architecture.md +35 -18
- package/docs/reset-lifecycle.md +16 -14
- package/package.json +11 -10
- package/src/context/budget.ts +148 -0
- package/src/context/context-window.ts +103 -0
- package/src/context/prompts.ts +111 -0
- package/src/context/reset-lifecycle.ts +145 -0
- package/src/context/runtime.ts +246 -0
- package/src/context/thresholds.ts +78 -0
- package/src/dream/cli.ts +1 -1
- package/src/dream/doctor.ts +27 -6
- package/src/dream/runner.ts +1 -1
- package/src/dream/settings.ts +32 -0
- package/src/{history-tools.ts → history/history-tools.ts} +3 -3
- package/src/{history.ts → history/history.ts} +9 -48
- package/src/index.ts +27 -89
- package/src/notes/address.ts +82 -16
- package/src/notes/frontmatter.ts +20 -3
- package/src/notes/notes-snapshot.ts +40 -0
- package/src/notes/paths.ts +64 -7
- package/src/notes/session-replay.ts +53 -0
- package/src/notes/store.ts +78 -25
- package/src/notes/tools.ts +7 -7
- package/src/protocol.ts +7 -5
- package/src/settings.ts +20 -0
- package/src/tool-schema.ts +1 -2
- package/dist/src/budget.js +0 -65
- package/dist/src/notes/model.js +0 -101
- package/dist/src/prompts.js +0 -88
- package/dist/src/reset-lifecycle.js +0 -155
- package/dist/src/thresholds.js +0 -102
- package/dist/src/warning.js +0 -44
- package/dist/test/coherence.test.js +0 -371
- package/dist/test/history.test.js +0 -26
- package/dist/test/integration.test.js +0 -1759
- package/dist/test/pagination.property.test.js +0 -471
- package/src/budget.ts +0 -67
- package/src/notes/model.ts +0 -109
- package/src/prompts.ts +0 -91
- package/src/reset-lifecycle.ts +0 -173
- package/src/thresholds.ts +0 -110
- package/src/warning.ts +0 -46
package/dist/src/notes/tools.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Type } from "@earendil-works/pi-ai";
|
|
2
2
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { localIso } from "./
|
|
3
|
+
import { localIso } from "./frontmatter.js";
|
|
4
4
|
import { DEFAULT_READ_WINDOW_CHARS, MAX_READ_WINDOW_CHARS, middleTruncate, output, outputRaw, page, prefixFit, readCharacterWindow, readWindowBlock, withinTextBudget } from "../tool-output.js";
|
|
5
5
|
import { cursor, nullableString, positiveInteger, searchQueries, searchQuery } from "../tool-schema.js";
|
|
6
6
|
import { assertAddress } from "./address.js";
|
|
@@ -8,7 +8,7 @@ import { NoteError, editNote, listNotes, readNote, searchNotes, writeNote } from
|
|
|
8
8
|
const ORIGIN = Type.Optional(Type.Union([Type.Literal("user"), Type.Literal("self"), Type.Literal("external")], {
|
|
9
9
|
description: "Where the note's content came from. user: written or dictated by the human. self: written by you, the agent (default). external: anything else — third-party text, tool output, fetched material.",
|
|
10
10
|
}));
|
|
11
|
-
const ADDRESS_DESCRIPTION = "Address forms are bare `<vpath>` for this session, `@project/<vpath>` for this project
|
|
11
|
+
const ADDRESS_DESCRIPTION = "Address forms are bare `<vpath>` for this session, `@project/<vpath>` for this project, `@human/<vpath>` for the human's cross-project home, `@self/<vpath>` / `@agents/<name>/<vpath>` for agent homes, and `@model/<vpath>` / `@models/<name>/<vpath>` for model homes. `@self` and `@model` mean the current agent/model; the `<name>` forms name one absolutely. The word after `@` is always one of the reserved home names — names live at the second level, never `@faye/`. `@` means leaving home. Any other `@` prefix, or `@` inside a vpath, is a hard error. There is no cross-home fallback. Paths reject `..`, absolute paths, and backslashes. Homes you do not own (`@agents/<other>/`, `@models/<other>/`) are read-only.";
|
|
12
12
|
function failure(error) {
|
|
13
13
|
if (error instanceof NoteError) {
|
|
14
14
|
const payload = { error: error.message };
|
|
@@ -29,7 +29,7 @@ export function registerNotesTools(pi) {
|
|
|
29
29
|
const content = params.content;
|
|
30
30
|
try {
|
|
31
31
|
const destination = assertAddress(params.address);
|
|
32
|
-
writeNote(ctx, destination.path, content, { scope: destination.scope, origin: (params.origin ?? "self"), stale: params.stale });
|
|
32
|
+
writeNote(ctx, destination.path, content, { scope: destination.scope, who: destination.who, origin: (params.origin ?? "self"), stale: params.stale });
|
|
33
33
|
return output({ address: params.address, written: true });
|
|
34
34
|
}
|
|
35
35
|
catch (error) {
|
|
@@ -44,7 +44,7 @@ export function registerNotesTools(pi) {
|
|
|
44
44
|
async execute(_id, params, _signal, _update, ctx) {
|
|
45
45
|
try {
|
|
46
46
|
const destination = assertAddress(params.address);
|
|
47
|
-
const { applied, diff } = editNote(ctx, destination.path, destination.scope, params.edits, { origin: params.origin, stale: params.stale, replaceAll: params.replace_all });
|
|
47
|
+
const { applied, diff } = editNote(ctx, destination.path, destination.scope, params.edits, { origin: params.origin, stale: params.stale, replaceAll: params.replace_all }, destination.who);
|
|
48
48
|
return output({ address: params.address, applied, diff });
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
@@ -60,7 +60,7 @@ export function registerNotesTools(pi) {
|
|
|
60
60
|
let note;
|
|
61
61
|
try {
|
|
62
62
|
const destination = assertAddress(params.address);
|
|
63
|
-
note = readNote(ctx, destination.path, destination.scope);
|
|
63
|
+
note = readNote(ctx, destination.path, destination.scope, destination.who);
|
|
64
64
|
}
|
|
65
65
|
catch (error) {
|
|
66
66
|
return failure(error);
|
|
@@ -79,7 +79,7 @@ export function registerNotesTools(pi) {
|
|
|
79
79
|
}));
|
|
80
80
|
pi.registerTool(defineTool({
|
|
81
81
|
name: "notes_list", label: "Notes list",
|
|
82
|
-
description: `List note files as rows carrying address, updated_at, and stale, most recently updated first. ${ADDRESS_DESCRIPTION}
|
|
82
|
+
description: `List note files as rows carrying address, updated_at, and stale, most recently updated first. ${ADDRESS_DESCRIPTION} Listings merge your five reachable homes: this session, @project/, @human/, your @self home, and the current @model home; other agents and models appear only under an explicit glob (@agents/<name>/**, @models/<name>/**, or a glob in the name segment to scan a whole namespace).`,
|
|
83
83
|
parameters: Type.Object({ pattern: nullableString(), cursor: cursor(), max_results: positiveInteger() }, { additionalProperties: false }),
|
|
84
84
|
async execute(_id, params, _signal, _update, ctx) {
|
|
85
85
|
let rows;
|
|
@@ -100,7 +100,7 @@ export function registerNotesTools(pi) {
|
|
|
100
100
|
}));
|
|
101
101
|
pi.registerTool(defineTool({
|
|
102
102
|
name: "notes_search", label: "Notes search",
|
|
103
|
-
description: `Case-sensitive literal substring search over note bodies; query is one string or several (OR), each matched line appears once. ${ADDRESS_DESCRIPTION}
|
|
103
|
+
description: `Case-sensitive literal substring search over note bodies; query is one string or several (OR), each matched line appears once. ${ADDRESS_DESCRIPTION} Search merges the same five reachable homes as notes_list; explicit globs reach other agents and models. Patterns glob over full address strings. Each file entry carries matches_total, its full match count before capping. Each match carries line, text, offset_chars (a code-point offset into the serialized note returned by notes_read, at the earliest query match), and truncated.`,
|
|
104
104
|
parameters: Type.Object({ query: searchQuery(), pattern: nullableString(), cursor: cursor(), max_matches_per_file: positiveInteger(), max_files: positiveInteger() }, { additionalProperties: false }),
|
|
105
105
|
async execute(_id, params, _signal, _update, ctx) {
|
|
106
106
|
const queries = searchQueries(params.query);
|
package/dist/src/protocol.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export const STATE_TYPE = "pi-context/state";
|
|
2
1
|
export const NOTE_TYPE = "pi-context/note";
|
|
3
2
|
export const BOOT_TYPE = "pi-context/boot";
|
|
4
3
|
export const GUIDANCE_TYPE = "pi-context/guidance";
|
|
5
4
|
export const WARNING_TYPE = "pi-context/warning";
|
|
6
5
|
export const RESET_MARKER_TYPE = "pi-context/reset-marker";
|
|
7
6
|
export const CONTINUATION_TYPE = "pi-context/continuation";
|
|
8
|
-
export const RESET_V2 = "reset-v2";
|
|
9
7
|
export const MAX_NOTE_BYTES = 1_000_000;
|
|
10
8
|
export const POCKET_SESSION_LIMIT = 5;
|
|
11
9
|
export const POCKET_PROJECT_LIMIT = 2;
|
|
12
|
-
export const
|
|
10
|
+
export const POCKET_HUMAN_LIMIT = 2;
|
|
11
|
+
export const POCKET_AGENT_LIMIT = 1;
|
|
12
|
+
export const POCKET_MODEL_LIMIT = 1;
|
|
13
13
|
// Write-time cap on a virtual note path. Deliberately NOT enforced by assertVirtualPath:
|
|
14
14
|
// notesFromSession replays already-persisted operations, which must keep loading sessions
|
|
15
15
|
// that contain a longer legacy path. Reads and replay stay un-capped.
|
|
@@ -49,9 +49,11 @@ Use get_context_remaining to see how much of the window is left. When it runs ou
|
|
|
49
49
|
|
|
50
50
|
If <context_window> lists a Previous context window id, a reset just happened and the old conversation is not included. Read your note checkpoint first, then recover details through history_*: history_read directly when you know the window and item IDs, history_list or history_search to find them when you don't.
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Notes live in five homes, and the word after @ is always one of their reserved names — your own name and other people's names live at the second level (@agents/faye/, never @faye/). Bare names are this session; @project/<vpath> is this project's workspace; @human/<vpath> is the human's cross-project home; @self/<vpath> and @agents/<name>/<vpath> are agent homes; @model/<vpath> and @models/<name>/<vpath> are model homes. @self and @model are the only relative forms — the current agent, the current model — and listings never show them, only the resolved name. There is no cross-home fallback.
|
|
53
53
|
Session notes belong to this trip — the goal, the progress, the loose ends. The next window of THIS trip wakes to them; once the trip is over, nobody does.
|
|
54
54
|
@project notes hold facts about this project — architecture, conventions, workflows, deployment and environment details — for whoever works here next.
|
|
55
|
-
@
|
|
55
|
+
@human notes hold the human's durable preferences and standing rules, plus lessons that apply across projects — for every agent that serves this human, whoever is running. You write there as the human's scribe; what the human dictates carries origin: user. When the intended scope is unclear, keep the note in the narrowest stated scope rather than widening it.
|
|
56
|
+
@self notes are yours — your voice, your lessons, your gripes — for the next run of whoever you are. Other agents read yours by explicit address and never write them; you read theirs the same way. A note only its author would ever need belongs here, not in @human.
|
|
57
|
+
@model notes capture the substrate — how the current model actually behaves: context honesty, tool quirks, fallback patterns. @model resolves live, so what you learn on one model is filed under that model even when a fallback moves you mid-window.
|
|
56
58
|
${CONTEXT_WINDOW_PROTOCOL_CLOSE_TAG}`;
|
|
57
59
|
export const WARNING_PROMPT = "Your memory is about to be erased. Write the note. NOW. If it already exists, revise it with notes_edit (or rewrite it whole): the goal, decisions, progress, learnings, next steps, the skills you still need, the window ID and item ID of every relevant user request still being solved, and important actions/tool calls for future reference. Do not continue any task. Then call wipe_memory IMMEDIATELY — anything not in the note dies with the window.";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PI_CONTEXT_SETTINGS_KEY } from "./protocol.js";
|
|
2
|
+
function isSettingsObject(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
}
|
|
5
|
+
/** Read the raw "pi-context" object from one parsed settings scope. */
|
|
6
|
+
function piContextSettings(settings) {
|
|
7
|
+
if (!isSettingsObject(settings))
|
|
8
|
+
return {};
|
|
9
|
+
const value = settings[PI_CONTEXT_SETTINGS_KEY];
|
|
10
|
+
return isSettingsObject(value) ? value : {};
|
|
11
|
+
}
|
|
12
|
+
/** Merge the global and project "pi-context" objects per key; project wins, mirroring Pi's deep merge. */
|
|
13
|
+
export function mergePiContextSettings(globalSettings, projectSettings) {
|
|
14
|
+
const merged = { ...piContextSettings(globalSettings), ...piContextSettings(projectSettings) };
|
|
15
|
+
return { reminderMarginTokens: merged.reminderMarginTokens, dreamer: merged.dreamer };
|
|
16
|
+
}
|
package/dist/src/tool-schema.js
CHANGED
|
@@ -4,7 +4,7 @@ export const positiveInteger = () => Type.Optional(Type.Integer({ minimum: 1 }))
|
|
|
4
4
|
export const cursor = () => Type.Optional(Type.Integer({ minimum: 0, description: "Continuation cursor: pass the previous next_cursor back unchanged, with the same filters and ordering. Omit to start. next_cursor is null only when the set is exhausted." }));
|
|
5
5
|
export const recentFirst = () => Type.Optional(Type.Boolean({ description: "Return newest-first. Only an explicit false returns oldest-first. Defaults to true." }));
|
|
6
6
|
/** Role filter. `developer` is the known author for this extension's own custom entries. */
|
|
7
|
-
export const role = Type.Union([Type.Literal("user"), Type.Literal("assistant"), Type.Literal("tool_call"), Type.Literal("tool"), Type.Literal("system"), Type.Literal("developer"), Type.Null()], { description: "Filter by the item's role. Exactly six: \"user\" and \"assistant\" are a message's visible text (assistant text never contains tool calls); \"tool_call\" is one tool invocation (tool_name set, content = the call's JSON arguments); \"tool\" is one tool run's output (tool_name set); \"system\" is a native Pi compaction summary; \"developer\" is an entry this extension authored (boot, guidance, warning, continuation messages,
|
|
7
|
+
export const role = Type.Union([Type.Literal("user"), Type.Literal("assistant"), Type.Literal("tool_call"), Type.Literal("tool"), Type.Literal("system"), Type.Literal("developer"), Type.Null()], { description: "Filter by the item's role. Exactly six: \"user\" and \"assistant\" are a message's visible text (assistant text never contains tool calls); \"tool_call\" is one tool invocation (tool_name set, content = the call's JSON arguments); \"tool\" is one tool run's output (tool_name set); \"system\" is a native Pi compaction or branch summary; \"developer\" is an entry this extension authored (boot, guidance, warning, continuation messages, or any pi-context/* custom message)." });
|
|
8
8
|
/** Search query parameter: one literal, or several literals combined with OR. */
|
|
9
9
|
export const searchQuery = () => Type.Union([Type.String(), Type.Array(Type.String(), { minItems: 1 })]);
|
|
10
10
|
/**
|