pi-fold 4.0.0 → 4.1.1
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 +22 -10
- package/extensions/active-context.ts +585 -134
- package/extensions/index.js +7 -2
- package/extensions/lib/brief-text.ts +116 -0
- package/extensions/lib/canonical.ts +3 -18
- package/extensions/lib/curation.ts +78 -39
- package/extensions/lib/editor-ui.ts +468 -47
- package/extensions/lib/folding.ts +20 -2
- package/extensions/lib/instrumentation.ts +3 -1
- package/extensions/lib/live-settings.ts +73 -0
- package/extensions/lib/persistence.ts +37 -3
- package/extensions/lib/policy.ts +55 -12
- package/extensions/lib/selection.ts +9 -44
- package/extensions/lib/status-widget.ts +175 -0
- package/extensions/lib/tool-surface.ts +0 -1
- package/extensions/lib/transcript.ts +43 -0
- package/extensions/settings.ts +104 -30
- package/package.json +5 -5
package/extensions/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export {
|
|
|
11
11
|
|
|
12
12
|
const PUBLIC_OPTIONS = Object.freeze([
|
|
13
13
|
"thresholds", "providerInputBudget", "blacklistAutoFoldTools", "toolFoldThreshold",
|
|
14
|
-
"
|
|
14
|
+
"preCommitNotice", "noticeLeadShare",
|
|
15
15
|
]);
|
|
16
16
|
|
|
17
17
|
const GENERATOR_DELETED = "the model brief generator is deleted (2026-08-14): across 15 sealed " +
|
|
@@ -32,8 +32,13 @@ const REFUSED_OPTIONS = Object.freeze({
|
|
|
32
32
|
guidance: "deleted 2026-08-23: it switched the copy that taught the agent to " +
|
|
33
33
|
"CHOOSE SPANS, and the agent does not choose spans. The runtime cuts at the frontier and " +
|
|
34
34
|
"the agent annotates what it cut, so the guidance it switched is replaced wholesale by " +
|
|
35
|
-
"the
|
|
35
|
+
"the pre-commit notice. That notice has its own option, preCommitNotice, which defaults " +
|
|
36
36
|
"false: the deterministic shape is what the campaign measured",
|
|
37
|
+
postFoldNotice: "renamed preCommitNotice on 2026-08-30, and the carrier it switches has " +
|
|
38
|
+
"changed with it: it fires on occupancy approaching a commit rather than on a count of " +
|
|
39
|
+
"unbriefed folds, and it STATES the window's status rather than asking the model for " +
|
|
40
|
+
"briefs, which sol-20260826-full2 scored at 3 correct against 9 wrong. The default is " +
|
|
41
|
+
"still false; noticeLeadShare sets how far ahead of the commit it speaks",
|
|
37
42
|
toolName: "the deployment identity is hardwired to pi-fold: the tool is pi_fold_context",
|
|
38
43
|
toolLabel: "the deployment identity is hardwired to pi-fold",
|
|
39
44
|
brandNoun: "the deployment identity is hardwired to pi-fold",
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/* THE BOUNDING PRIMITIVES. Pure string rules, no fold state and no host types, so a
|
|
2
|
+
second consumer can hold the same rules rather than a second implementation of them.
|
|
3
|
+
Extracted 2026-08-30 when the DeepSeek Harness build became the third runtime that
|
|
4
|
+
needs them: dsh-fold mirrors this file and nothing else from lib.
|
|
5
|
+
|
|
6
|
+
Every deterministic cut in the runtime runs through boundedSubject so a bound always
|
|
7
|
+
leaves its mark: "..." means exactly "content continues in the exact source", and a
|
|
8
|
+
reader never mistakes a cut value for a complete one. oneLine collapses whitespace
|
|
9
|
+
then delegates; factualValue sanitizes then delegates through oneLine. No other site
|
|
10
|
+
slices. Gate 136 pins this.
|
|
11
|
+
|
|
12
|
+
The two brief predicates take their bounds as required arguments here. Their callers
|
|
13
|
+
in canonical.ts re-export them with the policy defaults applied, so every existing
|
|
14
|
+
call site is unchanged and the policy stays owned by policy.ts. */
|
|
15
|
+
|
|
16
|
+
export function boundedSubject(text: string, budget: number): string {
|
|
17
|
+
if (text.length <= budget) return text;
|
|
18
|
+
let kept = text.slice(0, Math.max(0, budget - 3)).trimEnd();
|
|
19
|
+
const finalCode = kept.charCodeAt(kept.length - 1);
|
|
20
|
+
if (finalCode >= 0xd800 && finalCode <= 0xdbff) kept = kept.slice(0, -1);
|
|
21
|
+
return `${kept}...`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function oneLine(value: string, maximum: number): string {
|
|
25
|
+
return boundedSubject(value.replace(/\s+/g, " ").trim(), maximum);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* THE TOOL-CLIP VIEW HEAD (2026-08-24). A clipped result keeps its opening verbatim,
|
|
29
|
+
not collapsed: the view stays a readable result head in place, unlike a brief's
|
|
30
|
+
one-line quote. The paragraph rule is gate 134's: an opening block terminated by a
|
|
31
|
+
blank line is kept whole up to the cap; a result that is bulk from line one keeps its
|
|
32
|
+
first cap's worth. The caller states the cut, per gate 136: the marker rides beside
|
|
33
|
+
the count of what it hides, never silently. */
|
|
34
|
+
export function toolClipHead(text: string, cap: number): string {
|
|
35
|
+
const lines = String(text ?? "").split(/\r?\n/);
|
|
36
|
+
const kept: string[] = [];
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
if (line.trim()) kept.push(line);
|
|
39
|
+
else if (kept.length) break;
|
|
40
|
+
}
|
|
41
|
+
const paragraph = kept.join("\n");
|
|
42
|
+
const head = paragraph.length > 0 && paragraph.length <= cap
|
|
43
|
+
? paragraph
|
|
44
|
+
: String(text ?? "").slice(0, cap);
|
|
45
|
+
return head;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function usefulBriefWithin(value: unknown, maximum: number, toolName: string): value is string {
|
|
49
|
+
if (typeof value !== "string") return false;
|
|
50
|
+
const brief = value.trim();
|
|
51
|
+
if (!brief || brief.length > maximum) return false;
|
|
52
|
+
const factualLines: string[] = [];
|
|
53
|
+
const lines = brief.split(/\r?\n/);
|
|
54
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
55
|
+
const text = lines[index].trim();
|
|
56
|
+
if (!text || text.toLowerCase().includes(toolName.toLowerCase()) ||
|
|
57
|
+
/^\[[^\]]*(?:fold|active-context)[^\]]*\]$/i.test(text)) continue;
|
|
58
|
+
if (/^(?:topology\b|expand\b|refold\b|list(?:\/page)?\b|page\b|action\b|fold(?:ed)?\s+(?:placeholder|uid|id)\b|(?:this\s+)?fold\b.*\b(?:expand|refold|placeholder|topology|uid|id)\b|(?:parent|children?|previous|next|navigation)\s*[:=])/i.test(text)) continue;
|
|
59
|
+
factualLines.push(text);
|
|
60
|
+
}
|
|
61
|
+
const factual = factualLines.join(" ").trim();
|
|
62
|
+
return /[A-Za-z0-9]{3,}/.test(factual);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function structurallyValidBriefWithin(value: unknown, maximum: number): value is string {
|
|
66
|
+
if (typeof value !== "string") return false;
|
|
67
|
+
const brief = value.trim();
|
|
68
|
+
return Boolean(brief) && brief.length <= maximum &&
|
|
69
|
+
(brief.match(/[A-Za-z0-9]/g)?.length ?? 0) >= 3;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* THE DIVIDED SEATING (extracted 2026-08-30 for the same second consumer). A brief that
|
|
73
|
+
names several subjects does NOT concatenate and slice: a slice at the cap drops whole
|
|
74
|
+
subjects with no statement that it did, which is the silent-cut defect gate 136 exists
|
|
75
|
+
to refuse, one level up. Instead the room left by the lead is divided.
|
|
76
|
+
|
|
77
|
+
Two bounds, in order. The count-slice first: room admits at most one subject per
|
|
78
|
+
minSubjectChars, because a share below that names nothing a reader can use, and every
|
|
79
|
+
subject past that count is DROPPED AND COUNTED in the tail. Then the char division
|
|
80
|
+
over the subjects that did seat, ascending by length so a short subject claims only
|
|
81
|
+
what it needs and returns the rest to the pool; the longest subject is bounded last
|
|
82
|
+
against everything the shorter ones gave back, which is what lets a note shorter than
|
|
83
|
+
its fair share ride whole. Ties hold source order, so the output is deterministic.
|
|
84
|
+
|
|
85
|
+
Every cut inside a seated subject goes through boundedSubject, so it carries the
|
|
86
|
+
marker. The caller owns the lead, the minimum and the noun the tail counts, because
|
|
87
|
+
those are policy; the division is not. */
|
|
88
|
+
export function seatSubjects(
|
|
89
|
+
subjects: readonly string[],
|
|
90
|
+
lead: string,
|
|
91
|
+
options: {
|
|
92
|
+
readonly total: number;
|
|
93
|
+
readonly minSubjectChars: number;
|
|
94
|
+
readonly omittedNoun: string;
|
|
95
|
+
readonly separator?: string;
|
|
96
|
+
},
|
|
97
|
+
): string {
|
|
98
|
+
const separator = options.separator ?? " | ";
|
|
99
|
+
const room = options.total - lead.length - 1;
|
|
100
|
+
const named = Math.max(1, Math.min(subjects.length,
|
|
101
|
+
Math.floor((room + separator.length) / (options.minSubjectChars + separator.length))));
|
|
102
|
+
const omitted = subjects.length - named;
|
|
103
|
+
const tail = omitted ? `${separator}${omitted} more ${options.omittedNoun}` : "";
|
|
104
|
+
const budget = room - tail.length - separator.length * (named - 1);
|
|
105
|
+
const order = subjects.slice(0, named).map((text, index) => ({ text, index })).sort((a, b) =>
|
|
106
|
+
a.text.length - b.text.length || a.index - b.index);
|
|
107
|
+
const bounded = new Array<string>(named);
|
|
108
|
+
let left = budget;
|
|
109
|
+
for (let taken = 0; taken < order.length; taken += 1) {
|
|
110
|
+
const owed = Math.max(1, Math.floor(left / (order.length - taken)));
|
|
111
|
+
const kept = boundedSubject(order[taken].text, owed);
|
|
112
|
+
bounded[order[taken].index] = kept;
|
|
113
|
+
left -= kept.length;
|
|
114
|
+
}
|
|
115
|
+
return `${lead}${bounded.join(separator)}${tail}.`.replace(/\s+/g, " ").trim();
|
|
116
|
+
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
stableStringify,
|
|
6
6
|
} from "../json.ts";
|
|
7
7
|
import { ACTIVE_CONTEXT_POLICY, DEFAULT_ACTIVE_CONTEXT_TOOL_NAME } from "./policy.ts";
|
|
8
|
+
import { structurallyValidBriefWithin, usefulBriefWithin } from "./brief-text.ts";
|
|
8
9
|
import type { ActiveContextState } from "./policy.ts";
|
|
9
10
|
|
|
10
11
|
export function clone<T>(value: T): T {
|
|
@@ -185,25 +186,9 @@ export function usefulBrief(
|
|
|
185
186
|
maximum = ACTIVE_CONTEXT_POLICY.maxBriefChars,
|
|
186
187
|
toolName = DEFAULT_ACTIVE_CONTEXT_TOOL_NAME,
|
|
187
188
|
): value is string {
|
|
188
|
-
|
|
189
|
-
const brief = value.trim();
|
|
190
|
-
if (!brief || brief.length > maximum) return false;
|
|
191
|
-
const factualLines: string[] = [];
|
|
192
|
-
const lines = brief.split(/\r?\n/);
|
|
193
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
194
|
-
const text = lines[index].trim();
|
|
195
|
-
if (!text || text.toLowerCase().includes(toolName.toLowerCase()) ||
|
|
196
|
-
/^\[[^\]]*(?:fold|active-context)[^\]]*\]$/i.test(text)) continue;
|
|
197
|
-
if (/^(?:topology\b|expand\b|refold\b|list(?:\/page)?\b|page\b|action\b|fold(?:ed)?\s+(?:placeholder|uid|id)\b|(?:this\s+)?fold\b.*\b(?:expand|refold|placeholder|topology|uid|id)\b|(?:parent|children?|previous|next|navigation)\s*[:=])/i.test(text)) continue;
|
|
198
|
-
factualLines.push(text);
|
|
199
|
-
}
|
|
200
|
-
const factual = factualLines.join(" ").trim();
|
|
201
|
-
return /[A-Za-z0-9]{3,}/.test(factual);
|
|
189
|
+
return usefulBriefWithin(value, maximum, toolName);
|
|
202
190
|
}
|
|
203
191
|
|
|
204
192
|
export function structurallyValidBrief(value: unknown, maximum = ACTIVE_CONTEXT_POLICY.maxBriefChars): value is string {
|
|
205
|
-
|
|
206
|
-
const brief = value.trim();
|
|
207
|
-
return Boolean(brief) && brief.length <= maximum &&
|
|
208
|
-
(brief.match(/[A-Za-z0-9]/g)?.length ?? 0) >= 3;
|
|
193
|
+
return structurallyValidBriefWithin(value, maximum);
|
|
209
194
|
}
|
|
@@ -9,71 +9,110 @@ import {
|
|
|
9
9
|
} from "./policy.ts";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* THE
|
|
12
|
+
* THE PRE-COMMIT NOTICE: STATUS AT THE ONE MOMENT IT CAN STILL BE ACTED ON (2026-08-30).
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* WHAT THIS REPLACED, AND WHY. Until 2026-08-30 this carrier was a brief SOLICITATION.
|
|
15
|
+
* It fired on a count of unbriefed folds and its middle sentence read "You know why the
|
|
16
|
+
* span mattered and what you will want back from it; the automatic brief reads the span
|
|
17
|
+
* alone and does not." That is flattery in the shape of an instruction: it tells the
|
|
18
|
+
* model it holds knowledge the runtime lacks at a moment when its actual grounds are a
|
|
19
|
+
* fold id, a kind and a 160-character head. sol-20260826-full2 priced the result. Agent
|
|
20
|
+
* briefs drew 3 correct against 9 wrong, the worst of every draw on the campaign, with
|
|
21
|
+
* the errors tracking the annotations, which is why postFoldNotice was defaulted false
|
|
22
|
+
* on 2026-08-28 rather than fixed. Shane, 2026-08-30: agent-provided briefs "spent
|
|
23
|
+
* tokens to confuse themselves", so the carrier should be "less of a suggestion to add
|
|
24
|
+
* briefs and more informatory".
|
|
20
25
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
26
|
+
* SO IT STATES AND DOES NOT ASK. Every sentence here is a fact the runtime measured:
|
|
27
|
+
* where the commit fires, how far away it is, what is staged, how much that frees, what
|
|
28
|
+
* is pinned and what the pin costs. The verbs are a REFERENCE LIST, seated last and
|
|
29
|
+
* carrying no argument for using any of them. The agent is told what is true and what it
|
|
30
|
+
* may do; it is not told that it knows something.
|
|
31
|
+
*
|
|
32
|
+
* IT SPEAKS ONCE PER APPROACH. The trigger is occupancy, not a fold count, and the caller
|
|
33
|
+
* latches it on the crossing, so a window that sits in the band for twenty passes gets
|
|
34
|
+
* one notice rather than twenty. It is ephemeral, so it never accumulates.
|
|
35
|
+
*
|
|
36
|
+
* THREE CORRECTIONS OF FACT the old copy could not carry, all of them load-bearing:
|
|
37
|
+
* pinning is not preservation (folding already preserves; a pin keeps bytes RAW AND IN
|
|
38
|
+
* VIEW and frees nothing), a staged span has not moved yet, and the headroom figure says
|
|
39
|
+
* whether it was measured or estimated rather than presenting either as the other.
|
|
24
40
|
*/
|
|
25
41
|
export function foldNoticeText(input: {
|
|
26
|
-
|
|
27
|
-
|
|
42
|
+
staged: ReadonlyArray<{ id: string; kind: string; tokens: number; brief?: string; briefed?: boolean }>;
|
|
43
|
+
freedTokens: number;
|
|
44
|
+
briefedCount: number;
|
|
45
|
+
pinnedEntries: number;
|
|
46
|
+
pinnedShare: number;
|
|
47
|
+
maxPinnedShare: number;
|
|
48
|
+
headroomTokens: number;
|
|
49
|
+
commitAtShare: number;
|
|
50
|
+
measured: boolean;
|
|
28
51
|
toolName: string;
|
|
29
52
|
brandNoun?: string;
|
|
30
53
|
}): string {
|
|
31
54
|
const brand = contextBrand(input.brandNoun ?? DEFAULT_ACTIVE_CONTEXT_BRAND_NOUN);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
const n = (value: number): string => Math.round(value).toLocaleString("en-US");
|
|
56
|
+
const pct = (share: number): string => `${Math.round(share * 100)}%`;
|
|
57
|
+
// THE STATUS IS SEATED BEFORE THE LIST, and the list is what gives when the notice runs
|
|
58
|
+
// out of room. Inherited unchanged from the solicitation, for the same reason: fold
|
|
59
|
+
// count grows without limit and the status does not, so a wide batch must not be able
|
|
60
|
+
// to push the facts off the end and leave a bare row of ids behind.
|
|
61
|
+
//
|
|
62
|
+
// MEASURED OR ESTIMATED, NAMED. The runtime's own law is that an estimate may schedule
|
|
63
|
+
// work and may not veto a request; scheduling this notice off an estimate is squarely
|
|
64
|
+
// inside that, but presenting an estimate as a count is not, so the basis rides along.
|
|
65
|
+
const status = [
|
|
66
|
+
`[${brand} folds] A commit fires at ${pct(input.commitAtShare)} of the serving budget: ` +
|
|
67
|
+
`about ${n(input.headroomTokens)} tokens of headroom left ` +
|
|
68
|
+
`(${input.measured ? "provider-measured" : "estimated; no provider count yet"}).`,
|
|
69
|
+
`Staged: ${n(input.staged.length)} fold${input.staged.length === 1 ? "" : "s"}` +
|
|
70
|
+
`, ${n(input.briefedCount)} carrying a brief you wrote` +
|
|
71
|
+
`, freeing about ${n(input.freedTokens)} tokens when the commit applies them.`,
|
|
72
|
+
];
|
|
73
|
+
if (input.pinnedEntries > 0) {
|
|
74
|
+
status.push(`Pinned: ${n(input.pinnedEntries)} ` +
|
|
75
|
+
`entr${input.pinnedEntries === 1 ? "y" : "ies"} held raw, ` +
|
|
76
|
+
`${pct(input.pinnedShare)} of the window against a ${pct(input.maxPinnedShare)} cap.`);
|
|
77
|
+
}
|
|
78
|
+
status.push(
|
|
79
|
+
"Nothing has moved yet: every byte these spans cover is still in front of you, and a " +
|
|
80
|
+
"staged fold enters your window only when the commit applies it. Folded material " +
|
|
81
|
+
"stays exactly recoverable through peek. A pin holds an entry raw and in view and " +
|
|
82
|
+
"frees nothing toward the commit, so pinned mass makes the rest fold sooner.",
|
|
83
|
+
`Verbs: ${input.toolName} {"action":"status"} · {"action":"brief","id":"<fold-id>","brief":"..."} · ` +
|
|
84
|
+
'{"action":"reboundary","ids":[...]} · {"action":"unmark","ids":["<fold-id>"]} · ' +
|
|
85
|
+
'{"action":"pin","ids":["<entry-id>"]} · {"action":"unpin","ids":["<entry-id>"]}.',
|
|
86
|
+
);
|
|
87
|
+
const head = status.join("\n");
|
|
51
88
|
// WHAT IT COULD NOT NAME, IT COUNTS (gate 136's law, and gate 115's shape one level
|
|
52
89
|
// out): a list that stops early and says nothing reads as a complete list.
|
|
53
90
|
// EACH ROW IDENTIFIES ITS SPAN. The pending mark carries the deterministic brief the
|
|
54
91
|
// runtime cut it with, and a notice that withholds it makes the agent guess: the
|
|
55
92
|
// dogfooded 1M session answered "I need to brief three pending folds. What are they?
|
|
56
93
|
// Likely: ..." from memory, and a wrong guess writes an accurate-sounding brief onto
|
|
57
|
-
// the wrong fold. The
|
|
94
|
+
// the wrong fold. The row is the identification, and it now names EVERY staged fold
|
|
95
|
+
// rather than only the unbriefed ones, because this is a status: a list that silently
|
|
96
|
+
// omitted the briefed folds would understate what the commit is about to take.
|
|
58
97
|
const NOTICE_ROW_CHARS = 160;
|
|
59
|
-
const entries = input.
|
|
60
|
-
.map((fold) => `${fold.id} (${fold.kind}, ~${fold.tokens} tokens
|
|
98
|
+
const entries = input.staged
|
|
99
|
+
.map((fold) => `${fold.id} (${fold.kind}, ~${fold.tokens} tokens` +
|
|
100
|
+
`${fold.briefed ? ", briefed" : ""})` +
|
|
61
101
|
(fold.brief ? `: ${oneLine(fold.brief, NOTICE_ROW_CHARS)}` : ""));
|
|
62
102
|
const overflowText = (seated: number) =>
|
|
63
|
-
`${input.toolName} {"action":"status"} lists the other ${input.
|
|
103
|
+
`${input.toolName} {"action":"status"} lists the other ${input.staged.length - seated}.`;
|
|
64
104
|
let seated = entries.length;
|
|
65
|
-
let list = `
|
|
105
|
+
let list = `Staged spans: ${entries.join("; ")}.`;
|
|
66
106
|
while (seated > 0 && Buffer.byteLength(`${head}\n${list}`, "utf8") > FOLD_NOTICE_BYTES) {
|
|
67
107
|
seated -= 1;
|
|
68
108
|
list = seated === 0
|
|
69
109
|
? overflowText(0)
|
|
70
|
-
: `
|
|
110
|
+
: `Staged spans: ${entries.slice(0, seated).join("; ")}. ${overflowText(seated)}`;
|
|
71
111
|
}
|
|
72
112
|
return boundReceiptText(`${head}\n${list}`, FOLD_NOTICE_BYTES,
|
|
73
|
-
`[${brand} folds]
|
|
113
|
+
`[${brand} folds] A commit is approaching; the staged list is unavailable this pass.`);
|
|
74
114
|
}
|
|
75
115
|
|
|
76
|
-
|
|
77
116
|
export interface ContextReceipt {
|
|
78
117
|
kind: string;
|
|
79
118
|
ordinal: number;
|