@treeseed/dsh-chapters 0.1.23 → 0.2.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 +2 -1
- package/cordis.patch.yml +54 -1
- package/docs/architecture.md +1 -466
- package/docs/backlog.md +234 -4
- package/docs/contract.md +127 -1
- package/docs/development.md +248 -12
- package/docs/host-compaction-seam.md +108 -0
- package/docs/knowledge-repo.md +532 -16
- package/docs/project-state.md +323 -0
- package/docs/verify.md +217 -0
- package/lib/archive.d.ts +49 -1
- package/lib/archive.js +56 -2
- package/lib/archive.js.map +1 -1
- package/lib/arrival.js +24 -10
- package/lib/arrival.js.map +1 -1
- package/lib/attach.d.ts +46 -0
- package/lib/attach.js +120 -0
- package/lib/attach.js.map +1 -0
- package/lib/auto-save.d.ts +145 -0
- package/lib/auto-save.js +318 -0
- package/lib/auto-save.js.map +1 -0
- package/lib/client.js +413 -11
- package/lib/client.js.map +1 -1
- package/lib/commands.d.ts +11 -1
- package/lib/commands.js +138 -7
- package/lib/commands.js.map +1 -1
- package/lib/compose.js +25 -6
- package/lib/compose.js.map +1 -1
- package/lib/continue-core.d.ts +24 -1
- package/lib/continue-core.js +41 -9
- package/lib/continue-core.js.map +1 -1
- package/lib/diag.d.ts +47 -0
- package/lib/diag.js +141 -0
- package/lib/diag.js.map +1 -0
- package/lib/engine-core.d.ts +51 -1
- package/lib/engine-core.js +70 -7
- package/lib/engine-core.js.map +1 -1
- package/lib/engine.d.ts +93 -0
- package/lib/engine.js +477 -36
- package/lib/engine.js.map +1 -1
- package/lib/enrich-queue.js +28 -2
- package/lib/enrich-queue.js.map +1 -1
- package/lib/enrich-wire.d.ts +92 -2
- package/lib/enrich-wire.js +135 -17
- package/lib/enrich-wire.js.map +1 -1
- package/lib/enrich.d.ts +27 -2
- package/lib/enrich.js +114 -18
- package/lib/enrich.js.map +1 -1
- package/lib/index.d.ts +59 -0
- package/lib/index.js +215 -2
- package/lib/index.js.map +1 -1
- package/lib/indexing.d.ts +7 -0
- package/lib/indexing.js.map +1 -1
- package/lib/injections.d.ts +1 -0
- package/lib/injections.js +38 -0
- package/lib/injections.js.map +1 -1
- package/lib/last-resort.d.ts +24 -0
- package/lib/last-resort.js +27 -0
- package/lib/last-resort.js.map +1 -0
- package/lib/prompt.d.ts +130 -0
- package/lib/prompt.js +255 -0
- package/lib/prompt.js.map +1 -0
- package/lib/registry.d.ts +39 -1
- package/lib/registry.js +76 -9
- package/lib/registry.js.map +1 -1
- package/lib/render.d.ts +31 -3
- package/lib/render.js +82 -3
- package/lib/render.js.map +1 -1
- package/lib/repair-run.d.ts +49 -0
- package/lib/repair-run.js +423 -0
- package/lib/repair-run.js.map +1 -0
- package/lib/repair.d.ts +200 -0
- package/lib/repair.js +354 -0
- package/lib/repair.js.map +1 -0
- package/lib/rules-commands.d.ts +22 -0
- package/lib/rules-commands.js +177 -20
- package/lib/rules-commands.js.map +1 -1
- package/lib/rules.d.ts +184 -7
- package/lib/rules.js +312 -27
- package/lib/rules.js.map +1 -1
- package/lib/save-core.d.ts +72 -0
- package/lib/save-core.js +220 -0
- package/lib/save-core.js.map +1 -0
- package/lib/session-rules.d.ts +222 -0
- package/lib/session-rules.js +454 -0
- package/lib/session-rules.js.map +1 -0
- package/lib/store.d.ts +22 -1
- package/lib/store.js +94 -17
- package/lib/store.js.map +1 -1
- package/lib/sync.js +13 -1
- package/lib/sync.js.map +1 -1
- package/lib/tools.d.ts +14 -0
- package/lib/tools.js +341 -91
- package/lib/tools.js.map +1 -1
- package/lib/types.d.ts +31 -1
- package/lib/types.js +12 -0
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +9 -0
- package/lib/version.js +10 -0
- package/lib/version.js.map +1 -0
- package/lib/warnings.d.ts +10 -0
- package/lib/warnings.js +18 -0
- package/lib/warnings.js.map +1 -0
- package/package.json +9 -3
package/lib/prompt.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { type SessionState } from './registry.ts';
|
|
2
|
+
import type { SessionEventLike } from './types.ts';
|
|
3
|
+
/** The stored override key (durable kv, same table as `enrichment.model`). */
|
|
4
|
+
export declare const PROMPT_SETTING_KEY = "prompt.model";
|
|
5
|
+
export interface PromptRoute {
|
|
6
|
+
provider: string;
|
|
7
|
+
model: string;
|
|
8
|
+
}
|
|
9
|
+
export interface PromptSuggestionResult {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PromptConfig {
|
|
14
|
+
/** config default for the model; '' ⇒ off unless a stored override says otherwise. */
|
|
15
|
+
model: string;
|
|
16
|
+
/** hard total budget on the assembled input slice. */
|
|
17
|
+
contextChars: number;
|
|
18
|
+
/** cap on the accepted recommendation; over-cap is invalid, never truncated. */
|
|
19
|
+
maxChars: number;
|
|
20
|
+
}
|
|
21
|
+
export interface PromptDeps {
|
|
22
|
+
getSetting(key: string): string | undefined;
|
|
23
|
+
putSetting(key: string, value: string): Promise<void>;
|
|
24
|
+
deleteSetting(key: string): Promise<void>;
|
|
25
|
+
/** Active chapter records for the calling session's registry row (never the
|
|
26
|
+
* ancestor walk — the live tail carries the immediate context; this is the
|
|
27
|
+
* economy, and the choice is documented at the site that reads it). */
|
|
28
|
+
chaptersOf(sessionId: string): Promise<{
|
|
29
|
+
number: number;
|
|
30
|
+
title: string;
|
|
31
|
+
summary: string;
|
|
32
|
+
}[]>;
|
|
33
|
+
config: PromptConfig;
|
|
34
|
+
/** The last route the conversation used on this plane, or null. */
|
|
35
|
+
conversationRoute(): PromptRoute | null;
|
|
36
|
+
/** One auxiliary completion; may reject — `suggest` catches and refuses honestly. */
|
|
37
|
+
fetch(prompt: string, route: PromptRoute): Promise<string>;
|
|
38
|
+
warn?(msg: string): void;
|
|
39
|
+
}
|
|
40
|
+
/** A live session as the command hands it over: id, an optional title, and the
|
|
41
|
+
* surface events (what the working model sees — the same view the defining model reads). */
|
|
42
|
+
export interface SuggestSession {
|
|
43
|
+
id: string;
|
|
44
|
+
title?: string | undefined;
|
|
45
|
+
events: SessionEventLike[];
|
|
46
|
+
}
|
|
47
|
+
/** The surface the host command and `/chapters-status` speak to. */
|
|
48
|
+
export interface PromptSurface {
|
|
49
|
+
/** Effective spec after precedence; '' ⇒ OFF. */
|
|
50
|
+
effective(): {
|
|
51
|
+
spec: string;
|
|
52
|
+
source: 'override' | 'config' | 'unset';
|
|
53
|
+
};
|
|
54
|
+
setModel(spec: string | null): Promise<void>;
|
|
55
|
+
suggest(session: SuggestSession): Promise<PromptSuggestionResult>;
|
|
56
|
+
/** One line for /chapters-status — ON names the model, OFF names the command. */
|
|
57
|
+
statusLine(): string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Effective spec = stored override ?? config default, trimmed. `undefined`
|
|
61
|
+
* (row absent — cleared) falls back; a STORED '' is the deliberate OFF.
|
|
62
|
+
*/
|
|
63
|
+
export declare function effectivePromptSpec(setting: string | undefined, configModel: string): {
|
|
64
|
+
spec: string;
|
|
65
|
+
source: 'override' | 'config' | 'unset';
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* 'provider/model' routes as named; a bare model name rides the conversation's
|
|
69
|
+
* provider (the enrich-wire rule); '' is not a route. A bare name with no
|
|
70
|
+
* provider answers with its reason rather than guessing.
|
|
71
|
+
*/
|
|
72
|
+
export declare function resolvePromptRoute(spec: string, conversation: PromptRoute | null): {
|
|
73
|
+
route: PromptRoute | null;
|
|
74
|
+
reason?: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* The conversation tail from surface events, screened through the ONE injection
|
|
78
|
+
* predicate (docs/contract.md, #59): injected events drop, injected spans strip.
|
|
79
|
+
* Chronological; text-bearing human/assistant messages only.
|
|
80
|
+
*/
|
|
81
|
+
export declare function tailFromEvents(events: SessionEventLike[]): {
|
|
82
|
+
role: 'user' | 'assistant';
|
|
83
|
+
text: string;
|
|
84
|
+
}[];
|
|
85
|
+
export interface SuggestFacts {
|
|
86
|
+
title?: string | undefined;
|
|
87
|
+
chapters: {
|
|
88
|
+
number: number;
|
|
89
|
+
title: string;
|
|
90
|
+
summary: string;
|
|
91
|
+
}[];
|
|
92
|
+
tail: {
|
|
93
|
+
role: 'user' | 'assistant';
|
|
94
|
+
text: string;
|
|
95
|
+
}[];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Assemble the auxiliary input: deterministic (same facts ⇒ same bytes), hard
|
|
99
|
+
* total ≤ `capChars`. Oldest chapter lines drop first, then oldest tail
|
|
100
|
+
* messages; every drop is ANNOUNCED in the input itself — a silent squeeze
|
|
101
|
+
* would make the answer look derived from bytes that were never sent.
|
|
102
|
+
*/
|
|
103
|
+
export declare function buildSuggestInput(facts: SuggestFacts, capChars: number, maxChars: number): string;
|
|
104
|
+
/**
|
|
105
|
+
* Validate one answer. A wrapping code fence or one pair of wrapping quotes is
|
|
106
|
+
* formatting noise and comes off; the rest is the human's next prompt verbatim.
|
|
107
|
+
* Over-cap or empty is INVALID (never truncated) — the caller retries once and
|
|
108
|
+
* then refuses with the measured numbers.
|
|
109
|
+
*/
|
|
110
|
+
export declare function validateSuggestion(raw: string, maxChars: number): {
|
|
111
|
+
ok: true;
|
|
112
|
+
text: string;
|
|
113
|
+
} | {
|
|
114
|
+
ok: false;
|
|
115
|
+
reason: string;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* The watermarks this surface caches per session: the seq of the last surface
|
|
119
|
+
* event at generation. A new completed turn moves the seq and retires the
|
|
120
|
+
* suggestion; a restart simply loses the cache (it is a convenience, never truth).
|
|
121
|
+
*/
|
|
122
|
+
export declare function createPromptSurface(deps: PromptDeps): PromptSurface;
|
|
123
|
+
/** Narrow slice: what `registerHostCommands` needs from the surface. */
|
|
124
|
+
export type PromptCommandSurface = PromptSurface;
|
|
125
|
+
/** Active chapters of one registry row, mapped to the facts the input uses. */
|
|
126
|
+
export declare function promptChaptersOf(state: SessionState): {
|
|
127
|
+
number: number;
|
|
128
|
+
title: string;
|
|
129
|
+
summary: string;
|
|
130
|
+
}[];
|
package/lib/prompt.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-chapters — prompt recommendations (#71, site §28).
|
|
3
|
+
*
|
|
4
|
+
* One model reads the context and DOES the work; a second model reads the context
|
|
5
|
+
* and DEFINES it: the next prompt the human would send, offered as a ghost layer
|
|
6
|
+
* in the composer. Off by default, and the model IS the switch: the effective spec
|
|
7
|
+
* (stored override ?? config `promptModel`) being non-empty is what enables the
|
|
8
|
+
* feature — one knob, no second boolean that could drift against it.
|
|
9
|
+
*
|
|
10
|
+
* Transport here is the PULL: the client overlay executes
|
|
11
|
+
* `/chapters-prompt suggest` over `remote.commands` (the door-proven wire) and this
|
|
12
|
+
* module answers from a one-per-turn-boundary cache or one bounded auxiliary call.
|
|
13
|
+
* The 0.2.x push upgrade (plugin record + projection) is in docs/backlog.md; the
|
|
14
|
+
* measured host facts are in docs/contract.md § Composer Ghost Layer.
|
|
15
|
+
*
|
|
16
|
+
* Invariants the answer MUST respect: the recommendation is UI state only. It is
|
|
17
|
+
* never archived, never enters a notice or a rule, never steers a model request —
|
|
18
|
+
* it reaches the log only when a human accepts it and sends, as their own message.
|
|
19
|
+
*
|
|
20
|
+
* @module dsh-chapters/src/prompt
|
|
21
|
+
*/
|
|
22
|
+
import { textOfBlocks } from "./log-shape.js";
|
|
23
|
+
import { screenConversation, stripReminderSpans } from "./injections.js";
|
|
24
|
+
import { isActiveChapter } from "./registry.js";
|
|
25
|
+
/** The stored override key (durable kv, same table as `enrichment.model`). */
|
|
26
|
+
export const PROMPT_SETTING_KEY = 'prompt.model';
|
|
27
|
+
/**
|
|
28
|
+
* Effective spec = stored override ?? config default, trimmed. `undefined`
|
|
29
|
+
* (row absent — cleared) falls back; a STORED '' is the deliberate OFF.
|
|
30
|
+
*/
|
|
31
|
+
export function effectivePromptSpec(setting, configModel) {
|
|
32
|
+
if (setting !== undefined)
|
|
33
|
+
return { spec: setting.trim(), source: 'override' };
|
|
34
|
+
const c = configModel.trim();
|
|
35
|
+
return { spec: c, source: c === '' ? 'unset' : 'config' };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 'provider/model' routes as named; a bare model name rides the conversation's
|
|
39
|
+
* provider (the enrich-wire rule); '' is not a route. A bare name with no
|
|
40
|
+
* provider answers with its reason rather than guessing.
|
|
41
|
+
*/
|
|
42
|
+
export function resolvePromptRoute(spec, conversation) {
|
|
43
|
+
if (spec === '')
|
|
44
|
+
return { route: null, reason: 'prompt suggestions are off' };
|
|
45
|
+
const i = spec.indexOf('/');
|
|
46
|
+
if (i > 0 && i < spec.length - 1)
|
|
47
|
+
return { route: { provider: spec.slice(0, i), model: spec.slice(i + 1) } };
|
|
48
|
+
if (conversation !== null && conversation.provider !== '')
|
|
49
|
+
return { route: { provider: conversation.provider, model: spec } };
|
|
50
|
+
return { route: null, reason: `no provider to route "${spec}" — name it as provider/model` };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The conversation tail from surface events, screened through the ONE injection
|
|
54
|
+
* predicate (docs/contract.md, #59): injected events drop, injected spans strip.
|
|
55
|
+
* Chronological; text-bearing human/assistant messages only.
|
|
56
|
+
*/
|
|
57
|
+
export function tailFromEvents(events) {
|
|
58
|
+
const out = [];
|
|
59
|
+
for (const ev of events) {
|
|
60
|
+
const data = (ev.data ?? {});
|
|
61
|
+
if (ev.type === 'user/message') {
|
|
62
|
+
const screen = screenConversation(data, textOfBlocks(data.content), 'user');
|
|
63
|
+
if (screen.omittedEvent)
|
|
64
|
+
continue;
|
|
65
|
+
const text = stripReminderSpans(screen.text).text.trim();
|
|
66
|
+
if (text !== '')
|
|
67
|
+
out.push({ role: 'user', text });
|
|
68
|
+
}
|
|
69
|
+
else if (ev.type === 'assistant/message') {
|
|
70
|
+
const message = (data.message ?? data);
|
|
71
|
+
const text = stripReminderSpans(textOfBlocks(message.content)).text.trim();
|
|
72
|
+
if (text !== '')
|
|
73
|
+
out.push({ role: 'assistant', text });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** One message's inline budget before its own ellipsis — the cap is a cap, the
|
|
79
|
+
* ellipsis says so (loudness, #61). */
|
|
80
|
+
const MESSAGE_INLINE_CHARS = 500;
|
|
81
|
+
/**
|
|
82
|
+
* Assemble the auxiliary input: deterministic (same facts ⇒ same bytes), hard
|
|
83
|
+
* total ≤ `capChars`. Oldest chapter lines drop first, then oldest tail
|
|
84
|
+
* messages; every drop is ANNOUNCED in the input itself — a silent squeeze
|
|
85
|
+
* would make the answer look derived from bytes that were never sent.
|
|
86
|
+
*/
|
|
87
|
+
export function buildSuggestInput(facts, capChars, maxChars) {
|
|
88
|
+
const chapters = [...facts.chapters];
|
|
89
|
+
let tail = [...facts.tail];
|
|
90
|
+
let droppedChapters = 0;
|
|
91
|
+
let droppedTail = 0;
|
|
92
|
+
let msgCap = MESSAGE_INLINE_CHARS;
|
|
93
|
+
const perMessage = (t) => (t.length > msgCap ? `${t.slice(0, msgCap - 1)}…` : t);
|
|
94
|
+
const render = (announce) => {
|
|
95
|
+
const head = `You are the second reader of a DSH session — a model that DEFINES the next step and never executes it.`;
|
|
96
|
+
const rules = `Write the ONE next prompt the human should send. A single imperative request; plain text; at most ${maxChars} characters; no preamble, no quotes, no markdown; it must follow from the latest state and propose nothing already done. Reply with ONLY that prompt.`;
|
|
97
|
+
const parts = [
|
|
98
|
+
head,
|
|
99
|
+
'',
|
|
100
|
+
`## Session${facts.title !== undefined && facts.title !== '' ? `: ${facts.title}` : ''}`,
|
|
101
|
+
'',
|
|
102
|
+
chapters.length > 0 ? '## Where it has been (chapters)' : '## Where it has been (no chapters yet)',
|
|
103
|
+
...chapters.map((c) => `${c.number}. ${c.title} — ${c.summary}`),
|
|
104
|
+
'',
|
|
105
|
+
tail.length > 0 ? '## Live tail (oldest first)' : '## Live tail (empty)',
|
|
106
|
+
...tail.map((m) => `${m.role === 'user' ? 'Human' : 'Assistant'}: ${perMessage(m.text)}`),
|
|
107
|
+
];
|
|
108
|
+
if (announce !== '')
|
|
109
|
+
parts.push(announce);
|
|
110
|
+
parts.push('', rules);
|
|
111
|
+
return parts.join('\n');
|
|
112
|
+
};
|
|
113
|
+
// Trim to budget: chapters are the compressible past; the tail is the present.
|
|
114
|
+
// The ladder has a floor: a `capChars` smaller than the fixed skeleton (persona +
|
|
115
|
+
// section headers + reply rule) cannot be met by dropping — the last arm returns
|
|
116
|
+
// the skeleton as-is. The documented default (4000) is far above it.
|
|
117
|
+
for (;;) {
|
|
118
|
+
const announce = droppedChapters + droppedTail === 0
|
|
119
|
+
? ''
|
|
120
|
+
: `\n(older context dropped to fit the ${capChars}-char budget: ${droppedChapters} chapter line(s), ${droppedTail} message(s))`;
|
|
121
|
+
const input = render(announce);
|
|
122
|
+
if (input.length <= capChars)
|
|
123
|
+
return input;
|
|
124
|
+
if (chapters.length > 0) {
|
|
125
|
+
chapters.shift();
|
|
126
|
+
droppedChapters += 1;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (tail.length > 1) {
|
|
130
|
+
tail.shift();
|
|
131
|
+
droppedTail += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (msgCap > 40) {
|
|
135
|
+
msgCap = Math.max(40, Math.floor(msgCap / 2));
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (tail.length === 1) {
|
|
139
|
+
tail = [];
|
|
140
|
+
droppedTail += 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
return input;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Validate one answer. A wrapping code fence or one pair of wrapping quotes is
|
|
148
|
+
* formatting noise and comes off; the rest is the human's next prompt verbatim.
|
|
149
|
+
* Over-cap or empty is INVALID (never truncated) — the caller retries once and
|
|
150
|
+
* then refuses with the measured numbers.
|
|
151
|
+
*/
|
|
152
|
+
export function validateSuggestion(raw, maxChars) {
|
|
153
|
+
let text = raw.trim();
|
|
154
|
+
const fence = text.match(/^`{2,}[^\n]*\n([\s\S]*?)\n`{2,}\s*$/);
|
|
155
|
+
if (fence?.[1] !== undefined)
|
|
156
|
+
text = fence[1].trim();
|
|
157
|
+
const quoted = text.match(/^"([\s\S]+)"$/) ?? text.match(/^“([\s\S]+)”$/);
|
|
158
|
+
if (quoted?.[1] !== undefined)
|
|
159
|
+
text = quoted[1].trim();
|
|
160
|
+
if (text === '')
|
|
161
|
+
return { ok: false, reason: 'the model returned no text' };
|
|
162
|
+
if (text.length > maxChars)
|
|
163
|
+
return { ok: false, reason: `the answer is ${text.length} characters, over the ${maxChars}-char cap` };
|
|
164
|
+
return { ok: true, text };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The watermarks this surface caches per session: the seq of the last surface
|
|
168
|
+
* event at generation. A new completed turn moves the seq and retires the
|
|
169
|
+
* suggestion; a restart simply loses the cache (it is a convenience, never truth).
|
|
170
|
+
*/
|
|
171
|
+
export function createPromptSurface(deps) {
|
|
172
|
+
const cache = new Map();
|
|
173
|
+
const inflight = new Map();
|
|
174
|
+
const refuse = (reason) => ({ ok: false, text: reason });
|
|
175
|
+
const effective = () => effectivePromptSpec(deps.getSetting(PROMPT_SETTING_KEY), deps.config.model);
|
|
176
|
+
const run = async (session, seq) => {
|
|
177
|
+
const { spec } = effective();
|
|
178
|
+
const { route, reason } = resolvePromptRoute(spec, deps.conversationRoute());
|
|
179
|
+
if (route === null)
|
|
180
|
+
return refuse(`prompt suggestions cannot run: ${reason ?? 'no route'}`);
|
|
181
|
+
let input;
|
|
182
|
+
let chaptersCount = 0;
|
|
183
|
+
try {
|
|
184
|
+
const chapters = await deps.chaptersOf(session.id);
|
|
185
|
+
chaptersCount = chapters.length;
|
|
186
|
+
const tail = tailFromEvents(session.events);
|
|
187
|
+
input = buildSuggestInput({ title: session.title, chapters, tail }, deps.config.contextChars, deps.config.maxChars);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
return refuse(`could not assemble the context: ${String(error?.message ?? error).slice(0, 160)}`);
|
|
191
|
+
}
|
|
192
|
+
const cap = `Remember: reply with ONLY the suggested prompt, plain text, at most ${deps.config.maxChars} characters.`;
|
|
193
|
+
try {
|
|
194
|
+
let text = await deps.fetch(input, route);
|
|
195
|
+
let verdict = validateSuggestion(text, deps.config.maxChars);
|
|
196
|
+
if (!verdict.ok) {
|
|
197
|
+
deps.warn?.(`prompt retry (${verdict.reason}) after ${chaptersCount} chapter line(s) + tail`);
|
|
198
|
+
text = await deps.fetch(`${input}\n${cap}`, route);
|
|
199
|
+
verdict = validateSuggestion(text, deps.config.maxChars);
|
|
200
|
+
}
|
|
201
|
+
if (!verdict.ok)
|
|
202
|
+
return refuse(`no recommendation: ${verdict.reason}`);
|
|
203
|
+
cache.set(session.id, { text: verdict.text, seq });
|
|
204
|
+
return { ok: true, text: verdict.text };
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
const message = `prompt model call failed: ${String(error?.message ?? error).slice(0, 160)}`;
|
|
208
|
+
deps.warn?.(message);
|
|
209
|
+
return refuse(message);
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
effective,
|
|
214
|
+
async setModel(spec) {
|
|
215
|
+
if (spec === null)
|
|
216
|
+
await deps.deleteSetting(PROMPT_SETTING_KEY);
|
|
217
|
+
else
|
|
218
|
+
await deps.putSetting(PROMPT_SETTING_KEY, spec.trim());
|
|
219
|
+
cache.clear();
|
|
220
|
+
inflight.clear();
|
|
221
|
+
},
|
|
222
|
+
async suggest(session) {
|
|
223
|
+
const { spec } = effective();
|
|
224
|
+
if (spec === '')
|
|
225
|
+
return refuse('prompt suggestions are off — enable with /chapters-prompt model <provider/model>');
|
|
226
|
+
const events = session.events;
|
|
227
|
+
if (events.length === 0)
|
|
228
|
+
return refuse('nothing to recommend yet — the session has no surface events');
|
|
229
|
+
const seq = events[events.length - 1].seq;
|
|
230
|
+
const tail = tailFromEvents(events);
|
|
231
|
+
if (!tail.some((m) => m.role === 'assistant'))
|
|
232
|
+
return refuse('nothing to recommend yet — no completed assistant turn');
|
|
233
|
+
const fresh = cache.get(session.id);
|
|
234
|
+
if (fresh !== undefined && fresh.seq === seq)
|
|
235
|
+
return { ok: true, text: fresh.text };
|
|
236
|
+
const pending = inflight.get(session.id);
|
|
237
|
+
if (pending !== undefined)
|
|
238
|
+
return await pending;
|
|
239
|
+
const job = run(session, seq).finally(() => { inflight.delete(session.id); });
|
|
240
|
+
inflight.set(session.id, job);
|
|
241
|
+
return await job;
|
|
242
|
+
},
|
|
243
|
+
statusLine() {
|
|
244
|
+
const { spec, source } = effective();
|
|
245
|
+
if (spec === '')
|
|
246
|
+
return `Prompt suggestions: OFF${source === 'override' ? ' (explicitly off — /chapters-prompt model <provider/model> to enable)' : ' — enable with /chapters-prompt model <provider/model>'}`;
|
|
247
|
+
return `Prompt suggestions: ON — ${spec} (${source}; the composer ghost offers its answer, accepted only by your keystroke)`;
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/** Active chapters of one registry row, mapped to the facts the input uses. */
|
|
252
|
+
export function promptChaptersOf(state) {
|
|
253
|
+
return state.chapters.filter(isActiveChapter).map((c) => ({ number: c.number, title: c.title, summary: c.summary }));
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}
|
package/lib/registry.d.ts
CHANGED
|
@@ -31,6 +31,15 @@ export interface SessionState {
|
|
|
31
31
|
*/
|
|
32
32
|
nextPlanNumber: number;
|
|
33
33
|
chapters: ChapterRecord[];
|
|
34
|
+
/**
|
|
35
|
+
* Write counter, bumped by the store on every accepted put (#59). It exists so a read-modify-write
|
|
36
|
+
* can DETECT that the row moved underneath it — the row is one whole value and five doors plus the
|
|
37
|
+
* turn/end listener write it, so "last writer wins" is not a theoretical concern: in the field a
|
|
38
|
+
* stale enrichment put erased a compaction's committed manifest, reservation, `finalized` entry and
|
|
39
|
+
* chapter record while leaving its file on disk. Optional-with-default so a stored row from before
|
|
40
|
+
* the field parses unchanged.
|
|
41
|
+
*/
|
|
42
|
+
rev?: number | undefined;
|
|
34
43
|
/** attemptKey -> numbers, so an idempotent retry reuses rather than duplicates. */
|
|
35
44
|
reservations: Record<string, number[]>;
|
|
36
45
|
/**
|
|
@@ -95,8 +104,37 @@ export declare function reservePlan(state: SessionState, attemptId: string, coun
|
|
|
95
104
|
state: SessionState;
|
|
96
105
|
numbers: number[];
|
|
97
106
|
};
|
|
98
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Append verified chapter records.
|
|
109
|
+
*
|
|
110
|
+
* Identity is `(number, sha256)`, never `number` alone (#59). An identical record is a no-op — that is
|
|
111
|
+
* what makes a retry after a crash safe. A record reusing a number for DIFFERENT bytes is not a retry,
|
|
112
|
+
* it is a collision: one door's reservation was rewound by a lost update and another door has now been
|
|
113
|
+
* handed a number whose file already exists. Measured in the field: chapter 15 was spent twice in
|
|
114
|
+
* `session-aac0254e`, and number-only dedupe silently DROPPED the second record while its 960 KB file
|
|
115
|
+
* stayed on disk — a chapter no TOC cites and no tool can find. Refusing is the only honest option.
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* The ONE test for "may this chapter be shown as knowledge?" (#60). Every derived layer — the TOC a
|
|
119
|
+
* child is seeded with, the enrichment queue, anything else that reads `state.chapters` — goes through
|
|
120
|
+
* this predicate, because a repair that left a husk in the corpus would be a lie a later reader cannot
|
|
121
|
+
* see past. Records written before this field existed are `undefined`, i.e. active: legacy archives
|
|
122
|
+
* are never silently hidden by a new filter.
|
|
123
|
+
*/
|
|
124
|
+
export declare function isActiveChapter(c: {
|
|
125
|
+
status?: 'superseded' | undefined;
|
|
126
|
+
}): boolean;
|
|
99
127
|
export declare function appendChapters(state: SessionState, records: readonly ChapterRecord[]): SessionState;
|
|
128
|
+
/**
|
|
129
|
+
* Free an attempt's retry key WITHOUT un-spacing its numbers (#59). Reserve-before-write exists so a
|
|
130
|
+
* crash cannot re-issue a number whose file may already be on disk; that guarantee must not be undone
|
|
131
|
+
* here. What this does is stop a burned key from living forever: a door that reserved and then failed
|
|
132
|
+
* (a read-back that did not verify, a refusal after writing) currently leaves `reservations[k]` behind
|
|
133
|
+
* with no file and no record, which is the state the field showed at numbers 20 and 37.
|
|
134
|
+
*
|
|
135
|
+
* `numbersSpent` stays monotonic — only the attempt key goes.
|
|
136
|
+
*/
|
|
137
|
+
export declare function releaseReservation(state: SessionState, attemptId: string): SessionState;
|
|
100
138
|
/**
|
|
101
139
|
* Commit point for a continuation: the child inherits the PARENT's root and
|
|
102
140
|
* gets its direct parent recorded. Set once, never rewritten — re-pointing a
|
package/lib/registry.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const freshSession = (sessionId) => ({
|
|
2
|
+
rev: 0,
|
|
2
3
|
parentSession: null,
|
|
3
4
|
rootSession: sessionId,
|
|
4
5
|
nextChapterNumber: 1,
|
|
@@ -36,11 +37,22 @@ export function reserve(state, attemptId, count) {
|
|
|
36
37
|
}
|
|
37
38
|
return { state, numbers: [...existing] };
|
|
38
39
|
}
|
|
39
|
-
|
|
40
|
+
// Never hand out a number the row already carries. A committed number with no matching
|
|
41
|
+
// reservation is a real state, not a bug: an orphan row written before this field existed, or a
|
|
42
|
+
// chapter whose record a lost update erased while its file survived (#59). Allocating into it would
|
|
43
|
+
// put two `0NN-*.md` in one tree, so allocation SKIPS it and spends the next free number instead.
|
|
44
|
+
const taken = new Set(state.chapters.map((c) => c.number));
|
|
45
|
+
const numbers = [];
|
|
46
|
+
let cursor = state.nextChapterNumber;
|
|
47
|
+
while (numbers.length < count) {
|
|
48
|
+
if (!taken.has(cursor))
|
|
49
|
+
numbers.push(cursor);
|
|
50
|
+
cursor += 1;
|
|
51
|
+
}
|
|
40
52
|
return {
|
|
41
53
|
state: {
|
|
42
54
|
...state,
|
|
43
|
-
nextChapterNumber:
|
|
55
|
+
nextChapterNumber: cursor,
|
|
44
56
|
reservations: { ...state.reservations, [attemptId]: numbers },
|
|
45
57
|
},
|
|
46
58
|
numbers,
|
|
@@ -59,23 +71,78 @@ export function reservePlan(state, attemptId, count) {
|
|
|
59
71
|
}
|
|
60
72
|
return { state, numbers: [...existing] };
|
|
61
73
|
}
|
|
62
|
-
|
|
74
|
+
// Same skip rule as reserve(): plan documents are numbered independently but the collision
|
|
75
|
+
// hazard is identical (a lost update rewinding nextPlanNumber re-issues `plan-003` over a twin).
|
|
76
|
+
const taken = new Set(state.chapters.flatMap((c) => c.planDocs ?? [])
|
|
77
|
+
.map((d) => /plan-(\d+)-/.exec(d.path)?.[1]).filter((n) => n !== undefined).map(Number));
|
|
78
|
+
const numbers = [];
|
|
79
|
+
let cursor = state.nextPlanNumber;
|
|
80
|
+
while (numbers.length < count) {
|
|
81
|
+
if (!taken.has(cursor))
|
|
82
|
+
numbers.push(cursor);
|
|
83
|
+
cursor += 1;
|
|
84
|
+
}
|
|
63
85
|
return {
|
|
64
86
|
state: {
|
|
65
87
|
...state,
|
|
66
|
-
nextPlanNumber:
|
|
88
|
+
nextPlanNumber: cursor,
|
|
67
89
|
reservations: { ...state.reservations, [key]: numbers },
|
|
68
90
|
},
|
|
69
91
|
numbers,
|
|
70
92
|
};
|
|
71
93
|
}
|
|
72
|
-
/**
|
|
94
|
+
/**
|
|
95
|
+
* Append verified chapter records.
|
|
96
|
+
*
|
|
97
|
+
* Identity is `(number, sha256)`, never `number` alone (#59). An identical record is a no-op — that is
|
|
98
|
+
* what makes a retry after a crash safe. A record reusing a number for DIFFERENT bytes is not a retry,
|
|
99
|
+
* it is a collision: one door's reservation was rewound by a lost update and another door has now been
|
|
100
|
+
* handed a number whose file already exists. Measured in the field: chapter 15 was spent twice in
|
|
101
|
+
* `session-aac0254e`, and number-only dedupe silently DROPPED the second record while its 960 KB file
|
|
102
|
+
* stayed on disk — a chapter no TOC cites and no tool can find. Refusing is the only honest option.
|
|
103
|
+
*/
|
|
104
|
+
/**
|
|
105
|
+
* The ONE test for "may this chapter be shown as knowledge?" (#60). Every derived layer — the TOC a
|
|
106
|
+
* child is seeded with, the enrichment queue, anything else that reads `state.chapters` — goes through
|
|
107
|
+
* this predicate, because a repair that left a husk in the corpus would be a lie a later reader cannot
|
|
108
|
+
* see past. Records written before this field existed are `undefined`, i.e. active: legacy archives
|
|
109
|
+
* are never silently hidden by a new filter.
|
|
110
|
+
*/
|
|
111
|
+
export function isActiveChapter(c) {
|
|
112
|
+
return c.status !== 'superseded';
|
|
113
|
+
}
|
|
73
114
|
export function appendChapters(state, records) {
|
|
74
|
-
const
|
|
75
|
-
const fresh =
|
|
76
|
-
|
|
115
|
+
const byNumber = new Map(state.chapters.map((c) => [c.number, c]));
|
|
116
|
+
const fresh = [];
|
|
117
|
+
for (const r of records) {
|
|
118
|
+
const prior = byNumber.get(r.number);
|
|
119
|
+
if (prior !== undefined) {
|
|
120
|
+
if (prior.sha256 !== r.sha256 || prior.path !== r.path) {
|
|
121
|
+
throw new Error(`appendChapters: chapter number ${r.number} is already committed with different content `
|
|
122
|
+
+ `(${prior.path} sha ${prior.sha256.slice(0, 12)}…) — refusing to overwrite or silently drop `
|
|
123
|
+
+ `${r.path} (sha ${r.sha256.slice(0, 12)}…). A reused number means a lost registry update (#59).`);
|
|
124
|
+
}
|
|
125
|
+
continue; // identical retry
|
|
126
|
+
}
|
|
127
|
+
fresh.push(r);
|
|
128
|
+
byNumber.set(r.number, r);
|
|
129
|
+
}
|
|
130
|
+
return fresh.length === 0 ? state : { ...state, chapters: [...state.chapters, ...fresh] };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Free an attempt's retry key WITHOUT un-spacing its numbers (#59). Reserve-before-write exists so a
|
|
134
|
+
* crash cannot re-issue a number whose file may already be on disk; that guarantee must not be undone
|
|
135
|
+
* here. What this does is stop a burned key from living forever: a door that reserved and then failed
|
|
136
|
+
* (a read-back that did not verify, a refusal after writing) currently leaves `reservations[k]` behind
|
|
137
|
+
* with no file and no record, which is the state the field showed at numbers 20 and 37.
|
|
138
|
+
*
|
|
139
|
+
* `numbersSpent` stays monotonic — only the attempt key goes.
|
|
140
|
+
*/
|
|
141
|
+
export function releaseReservation(state, attemptId) {
|
|
142
|
+
if (state.reservations[attemptId] === undefined)
|
|
77
143
|
return state;
|
|
78
|
-
|
|
144
|
+
const { [attemptId]: _gone, ...rest } = state.reservations;
|
|
145
|
+
return { ...state, reservations: rest };
|
|
79
146
|
}
|
|
80
147
|
/**
|
|
81
148
|
* Commit point for a continuation: the child inherits the PARENT's root and
|
package/lib/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAmEA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAiB,EAAgB,EAAE,CAAC,CAAC;IAChE,GAAG,EAAE,CAAC;IACN,aAAa,EAAE,IAAI;IACnB,WAAW,EAAE,SAAS;IACtB,iBAAiB,EAAE,CAAC;IACpB,cAAc,EAAE,CAAC;IACjB,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,EAAE;IACZ,YAAY,EAAE,EAAE;IAChB,KAAK,EAAE,EAAE;IACT,SAAS,EAAE,EAAE;CACd,CAAC,CAAA;AAgBF,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAC1B,KAAmB,EACnB,YAAoB,EACpB,IAAgG;IAEhG,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,CAAA;AACtE,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,aAAa,CAAC,KAAmB,EAAE,YAAoB,EAAE,OAA0B;IACjG,IAAI,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC7D,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,EAAE,CAAA;AACtF,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAmB,EAAE,YAAoB,EAAW,EAAE,CAChF,KAAK,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,SAAS,CAAA;AAE7C,wEAAwE;AACxE,MAAM,UAAU,OAAO,CAAC,KAAmB,EAAE,SAAiB,EAAE,KAAa;IAC3E,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,kDAAkD,KAAK,EAAE,CAAC,CAAA;IAC5E,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAA;IAC9C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC9B,yEAAyE;YACzE,oEAAoE;YACpE,yDAAyD;YACzD,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,mBAAmB,QAAQ,CAAC,MAAM,uBAAuB,KAAK,EAAE,CAAC,CAAA;QACjH,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAA;IAC1C,CAAC;IACD,uFAAuF;IACvF,gGAAgG;IAChG,oGAAoG;IACpG,kGAAkG;IAClG,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1D,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAA;IACpC,OAAO,OAAO,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,MAAM,IAAI,CAAC,CAAA;IACb,CAAC;IACD,OAAO;QACL,KAAK,EAAE;YACL,GAAG,KAAK;YACR,iBAAiB,EAAE,MAAM;YACzB,YAAY,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;SAC9D;QACD,OAAO;KACR,CAAA;AACH,CAAC;AACD,uEAAuE;AACvE,MAAM,UAAU,WAAW,CACzB,KAAmB,EACnB,SAAiB,EACjB,KAAa;IAEb,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,sDAAsD,KAAK,EAAE,CAAC,CAAA;IAChF,CAAC;IACD,MAAM,GAAG,GAAG,QAAQ,SAAS,EAAE,CAAA;IAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,mBAAmB,QAAQ,CAAC,MAAM,uBAAuB,KAAK,EAAE,CAAC,CAAA;QAC/G,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAA;IAC1C,CAAC;IACD,2FAA2F;IAC3F,iGAAiG;IACjG,MAAM,KAAK,GAAG,IAAI,GAAG,CACnB,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAAuD,CAAC,QAAQ,IAAI,EAAE,CAAC;SACnG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CACvG,CAAA;IACD,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,IAAI,MAAM,GAAG,KAAK,CAAC,cAAc,CAAA;IACjC,OAAO,OAAO,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAC5C,MAAM,IAAI,CAAC,CAAA;IACb,CAAC;IACD,OAAO;QACL,KAAK,EAAE;YACL,GAAG,KAAK;YACR,cAAc,EAAE,MAAM;YACtB,YAAY,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE;SACxD;QACD,OAAO;KACR,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,CAAwC;IACtE,OAAO,CAAC,CAAC,MAAM,KAAK,YAAY,CAAA;AAClC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAmB,EAAE,OAAiC;IACnF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IAClE,MAAM,KAAK,GAAoB,EAAE,CAAA;IACjC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;QACpC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;gBACvD,MAAM,IAAI,KAAK,CACb,kCAAkC,CAAC,CAAC,MAAM,+CAA+C;sBACvF,IAAI,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,8CAA8C;sBAC7F,GAAG,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,yDAAyD,CACnG,CAAA;YACH,CAAC;YACD,SAAQ,CAAC,kBAAkB;QAC7B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACb,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC3B,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,KAAK,CAAC,EAAE,CAAA;AAC3F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB,EAAE,SAAiB;IACvE,IAAI,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC7D,MAAM,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC,YAAY,CAAA;IAC1D,OAAO,EAAE,GAAG,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,CAAA;AACzC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAmB,EAAE,eAAuB,EAAE,iBAAyB;IAC/F,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,aAAa,KAAK,eAAe,IAAI,KAAK,CAAC,WAAW,KAAK,iBAAiB;YAAE,OAAO,KAAK,CAAA;QACpG,MAAM,IAAI,KAAK,CACb,+CAA+C,KAAK,CAAC,aAAa,UAAU,KAAK,CAAC,WAAW,IAAI;cAC/F,+BAA+B,eAAe,UAAU,iBAAiB,GAAG,CAC/E,CAAA;IACH,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAA;AACrF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAA6C,EAAE,SAAiB,EAAE,QAAQ,GAAG,IAAI;IAC5G,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,IAAI,MAAM,GAAuB,SAAS,CAAA;IAC1C,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAA;QAC9E,IAAI,IAAI,CAAC,IAAI,IAAI,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,EAAE,CAAC,CAAA;QACtF,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAChB,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,CAAA;QACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,MAAM,CAAC,CAAA,CAAC,wCAAwC;YAClF,MAAM,IAAI,KAAK,CAAC,0CAA0C,MAAM,wBAAwB,CAAC,CAAA;QAC3F,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QACpB,MAAM,GAAG,KAAK,CAAC,aAAa,IAAI,SAAS,CAAA;IAC3C,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAaD;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,IAAuB,EACvB,GAA6C,EAC7C,MAAuD;IAEvD,MAAM,OAAO,GAAiB,EAAE,CAAA;IAChC,KAAK,MAAM,SAAS,IAAI,IAAI,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,CAAA;QAC5B,IAAI,KAAK,KAAK,SAAS;YAAE,SAAQ;QACjC,KAAK,MAAM,OAAO,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9E,OAAO,CAAC,IAAI,CAAC;gBACX,aAAa,EAAE,SAAS;gBACxB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC;aACxB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,gBAAgB,CAAC,KAAmB,EAAE,GAAwB;IAC5E,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC5D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IAC5H,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,CAAA;AAC/D,CAAC"}
|
package/lib/render.d.ts
CHANGED
|
@@ -20,9 +20,37 @@ export declare function artifactRef(text: string, opts: {
|
|
|
20
20
|
* Validate model-supplied ranges. Never trust them (hard rule): an overlap duplicates archived text and a
|
|
21
21
|
* gap silently drops history, so both are errors here rather than data that quietly goes missing.
|
|
22
22
|
*/
|
|
23
|
-
export declare function validateRanges(chapters: ChapterRange[], archiveCeiling: number
|
|
24
|
-
/**
|
|
25
|
-
|
|
23
|
+
export declare function validateRanges(chapters: ChapterRange[], archiveCeiling: number,
|
|
24
|
+
/** The watermark: ranges must start HERE, not wherever the submitter liked (#59). */
|
|
25
|
+
archiveFloor?: number): ChapterRange[];
|
|
26
|
+
/**
|
|
27
|
+
* Ranges whose render blew past the oversize ceiling (#59). `chapterTokenTarget` stays the SOFT target
|
|
28
|
+
* — split at the next boundary, never clip — and a modest overshoot is a warning, because a single
|
|
29
|
+
* indivisible turn can legitimately exceed it. Past `target × factor` the chapter is not a chapter:
|
|
30
|
+
* nobody can `read` it back inside the window it exists for, which is the whole point of archiving.
|
|
31
|
+
* The field held an 89,982-token chapter, an unrepaired 237,870-token orphan and eleven over 32K.
|
|
32
|
+
*/
|
|
33
|
+
export declare function oversizeRanges(rendered: readonly {
|
|
34
|
+
range: ChapterRange;
|
|
35
|
+
stats: {
|
|
36
|
+
estimatedTokens: number;
|
|
37
|
+
};
|
|
38
|
+
}[], chapterTokenTarget: number, oversizeFactor: number): string[];
|
|
39
|
+
/**
|
|
40
|
+
* The capped public form (#61). `chapters_segment` once emitted 49,932 characters of its OWN output while
|
|
41
|
+
* also reporting `(Omitted 275266 bytes …)` — ≈81K est tokens into a 32K-class window, asking a small
|
|
42
|
+
* model to judge a candidate list it never received. Ranking is by size descending, so what a cap
|
|
43
|
+
* removes is the SMALL stuff: the judgment the model is actually asked for stays intact.
|
|
44
|
+
*
|
|
45
|
+
* The caller reports what was withheld — see `candidateTotals` — because a capped list that does not say
|
|
46
|
+
* it is capped is a silent truncation, and AGENTS.md forbids truncating to fit.
|
|
47
|
+
*/
|
|
48
|
+
export declare function toolResultCandidates(events: SessionEventLike[], range: ChapterRange, capTokens?: number): ToolResultCandidate[];
|
|
49
|
+
/** Totals for the WHOLE candidate set, so a capped response can name what it left out. */
|
|
50
|
+
export declare function candidateTotals(events: SessionEventLike[], range: ChapterRange): {
|
|
51
|
+
count: number;
|
|
52
|
+
bytes: number;
|
|
53
|
+
};
|
|
26
54
|
/**
|
|
27
55
|
* Render one chapter. `overrides` is the model's sparse exception list; anything not named follows the
|
|
28
56
|
* default rule (small results inline, large ones deferred). Either way the artifact is written, so a wrong
|