@treeseed/dsh-chapters 0.1.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/LICENSE +14 -0
- package/README.md +644 -0
- package/cordis.patch.yml +88 -0
- package/docs/architecture.md +460 -0
- package/docs/contract.md +375 -0
- package/docs/development.md +437 -0
- package/docs/host-compaction-seam.md +117 -0
- package/docs/knowledge-repo.md +730 -0
- package/docs/provider.md +98 -0
- package/docs/verify.md +200 -0
- package/lib/archive.d.ts +88 -0
- package/lib/archive.js +114 -0
- package/lib/archive.js.map +1 -0
- package/lib/arrival.d.ts +23 -0
- package/lib/arrival.js +86 -0
- package/lib/arrival.js.map +1 -0
- package/lib/artifact-query.d.ts +53 -0
- package/lib/artifact-query.js +130 -0
- package/lib/artifact-query.js.map +1 -0
- package/lib/client.js +234 -0
- package/lib/client.js.map +1 -0
- package/lib/commands.d.ts +28 -0
- package/lib/commands.js +305 -0
- package/lib/commands.js.map +1 -0
- package/lib/compose.d.ts +30 -0
- package/lib/compose.js +131 -0
- package/lib/compose.js.map +1 -0
- package/lib/continue-core.d.ts +153 -0
- package/lib/continue-core.js +252 -0
- package/lib/continue-core.js.map +1 -0
- package/lib/engine-core.d.ts +219 -0
- package/lib/engine-core.js +405 -0
- package/lib/engine-core.js.map +1 -0
- package/lib/engine.d.ts +211 -0
- package/lib/engine.js +571 -0
- package/lib/engine.js.map +1 -0
- package/lib/enrich-queue.d.ts +66 -0
- package/lib/enrich-queue.js +100 -0
- package/lib/enrich-queue.js.map +1 -0
- package/lib/enrich-store.d.ts +31 -0
- package/lib/enrich-store.js +103 -0
- package/lib/enrich-store.js.map +1 -0
- package/lib/enrich-wire.d.ts +73 -0
- package/lib/enrich-wire.js +209 -0
- package/lib/enrich-wire.js.map +1 -0
- package/lib/enrich.d.ts +73 -0
- package/lib/enrich.js +150 -0
- package/lib/enrich.js.map +1 -0
- package/lib/gitops.d.ts +107 -0
- package/lib/gitops.js +539 -0
- package/lib/gitops.js.map +1 -0
- package/lib/index.d.ts +81 -0
- package/lib/index.js +305 -0
- package/lib/index.js.map +1 -0
- package/lib/indexing.d.ts +85 -0
- package/lib/indexing.js +212 -0
- package/lib/indexing.js.map +1 -0
- package/lib/injections.d.ts +66 -0
- package/lib/injections.js +83 -0
- package/lib/injections.js.map +1 -0
- package/lib/notice.d.ts +60 -0
- package/lib/notice.js +84 -0
- package/lib/notice.js.map +1 -0
- package/lib/provider.d.ts +44 -0
- package/lib/provider.js +66 -0
- package/lib/provider.js.map +1 -0
- package/lib/redact.d.ts +24 -0
- package/lib/redact.js +85 -0
- package/lib/redact.js.map +1 -0
- package/lib/registry.d.ts +120 -0
- package/lib/registry.js +130 -0
- package/lib/registry.js.map +1 -0
- package/lib/render.d.ts +40 -0
- package/lib/render.js +280 -0
- package/lib/render.js.map +1 -0
- package/lib/repo.d.ts +78 -0
- package/lib/repo.js +184 -0
- package/lib/repo.js.map +1 -0
- package/lib/rules-commands.d.ts +24 -0
- package/lib/rules-commands.js +138 -0
- package/lib/rules-commands.js.map +1 -0
- package/lib/rules.d.ts +90 -0
- package/lib/rules.js +185 -0
- package/lib/rules.js.map +1 -0
- package/lib/search.d.ts +48 -0
- package/lib/search.js +152 -0
- package/lib/search.js.map +1 -0
- package/lib/signature.d.ts +28 -0
- package/lib/signature.js +149 -0
- package/lib/signature.js.map +1 -0
- package/lib/store.d.ts +130 -0
- package/lib/store.js +235 -0
- package/lib/store.js.map +1 -0
- package/lib/sync.d.ts +183 -0
- package/lib/sync.js +719 -0
- package/lib/sync.js.map +1 -0
- package/lib/tools.d.ts +84 -0
- package/lib/tools.js +581 -0
- package/lib/tools.js.map +1 -0
- package/lib/treedx/client.d.ts +57 -0
- package/lib/treedx/client.js +82 -0
- package/lib/treedx/client.js.map +1 -0
- package/lib/treedx/provider.d.ts +39 -0
- package/lib/treedx/provider.js +479 -0
- package/lib/treedx/provider.js.map +1 -0
- package/lib/treedx/state.d.ts +27 -0
- package/lib/treedx/state.js +65 -0
- package/lib/treedx/state.js.map +1 -0
- package/lib/types.d.ts +100 -0
- package/lib/types.js +19 -0
- package/lib/types.js.map +1 -0
- package/lib/vocabulary.d.ts +41 -0
- package/lib/vocabulary.js +118 -0
- package/lib/vocabulary.js.map +1 -0
- package/package.json +132 -0
- package/presets/chapters/agent.cordis.yml +272 -0
- package/presets/chapters/preset.yml +3 -0
- package/presets/probe-roots/agent.cordis.yml +11 -0
- package/presets/probe-roots/preset.yml +3 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* chapters_continue — pure orchestration of a citation-linked continuation,
|
|
3
|
+
* behind ports (docs/development.md § L0: this file imports no cordis and no
|
|
4
|
+
* @deepseek-ai package; `src/tools.ts` adapts it).
|
|
5
|
+
*
|
|
6
|
+
* Order is the atomicity contract (docs/architecture.md § Atomicity):
|
|
7
|
+
* validate ranges -> reserve -> write+verify files -> assemble the notice ->
|
|
8
|
+
* budget preflight -> CREATE the child -> commit registry links. A crash before
|
|
9
|
+
* creation leaves orphan files (harmless, numbers already spent); creation
|
|
10
|
+
* without the commit is retried idempotently via attemptKey + linkChild.
|
|
11
|
+
*
|
|
12
|
+
* Budget honesty: allowance is a share of the window REMAINING after the
|
|
13
|
+
* header, never of the whole window; the header is bounded by the last real
|
|
14
|
+
* request's full prompt (conservative — over-counting errs toward loud
|
|
15
|
+
* refusals, which is the designed direction). No usage yet -> the probe says
|
|
16
|
+
* `headerBoundTokens: null` and the allowance conservatively assumes a zero
|
|
17
|
+
* header only because a session with no requests cannot have a large TOC.
|
|
18
|
+
*/
|
|
19
|
+
import type { ChapterRange, RenderConfig, SessionEventLike, ToolResultOverride } from './types.ts';
|
|
20
|
+
import type { SessionState } from './registry.ts';
|
|
21
|
+
export interface ContinueArgs {
|
|
22
|
+
callerSessionId: string;
|
|
23
|
+
/** The caller's own preset id, resolved by the adapter from its session observation. */
|
|
24
|
+
callerPreset: string | null;
|
|
25
|
+
title: string;
|
|
26
|
+
handoffNote: string;
|
|
27
|
+
chapters: ChapterRange[];
|
|
28
|
+
toolResultOverrides: ToolResultOverride[];
|
|
29
|
+
/**
|
|
30
|
+
* The knowledge-project line for the notice (record §8): `Project: <slug> ·
|
|
31
|
+
* <key>` plus the search instruction. Set by the adapter from the linked
|
|
32
|
+
* project; absent → the notice simply omits the section.
|
|
33
|
+
*/
|
|
34
|
+
projectLine?: string;
|
|
35
|
+
/** P3 §7.3: rules block for the notice (see assembleNotice). */
|
|
36
|
+
rulesSection?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ContinueConfig extends RenderConfig {
|
|
39
|
+
artifactStoreRoot: string;
|
|
40
|
+
continuationBudgetRatio: number;
|
|
41
|
+
fallbackPreset: string;
|
|
42
|
+
/** Topic-sequential composition (record §4.2): merge while overlap ≥ τ. */
|
|
43
|
+
mergeThreshold: number;
|
|
44
|
+
/** Estimated-token cap per composed chapter. */
|
|
45
|
+
chapterLimit: number;
|
|
46
|
+
}
|
|
47
|
+
export interface BudgetProbe {
|
|
48
|
+
windowTokens: number | null;
|
|
49
|
+
headerBoundTokens: number | null;
|
|
50
|
+
}
|
|
51
|
+
export interface ContinuePorts {
|
|
52
|
+
/** The caller's durable events, oldest first (full log; the ceiling trims it). */
|
|
53
|
+
readCallerEvents(): Promise<SessionEventLike[]>;
|
|
54
|
+
getState(sessionId: string): Promise<SessionState>;
|
|
55
|
+
putState(sessionId: string, state: SessionState): Promise<void>;
|
|
56
|
+
/** ArchiveFs anchored at the caller's workspace cwd (the read tool reaches it). */
|
|
57
|
+
fs(): import('./archive.ts').ArchiveFs;
|
|
58
|
+
budgetProbe(): Promise<BudgetProbe>;
|
|
59
|
+
newId(): string;
|
|
60
|
+
/** agents.create + setup-mount + workspace attach; throws on kernel refusal. */
|
|
61
|
+
createChild(input: {
|
|
62
|
+
sessionId: string;
|
|
63
|
+
noticeEvent: SessionEventLike;
|
|
64
|
+
presetId: string;
|
|
65
|
+
title: string;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
now(): number;
|
|
68
|
+
}
|
|
69
|
+
export interface ContinueResult {
|
|
70
|
+
ok: boolean;
|
|
71
|
+
childSessionId?: string;
|
|
72
|
+
presetUsed?: string;
|
|
73
|
+
chapters?: Array<{
|
|
74
|
+
number: number;
|
|
75
|
+
path: string;
|
|
76
|
+
startSeq: number;
|
|
77
|
+
endSeq: number;
|
|
78
|
+
}>;
|
|
79
|
+
warnings?: string[];
|
|
80
|
+
/** Numbers behind every outcome — AGENTS: refuse WITH the numbers. */
|
|
81
|
+
budget?: {
|
|
82
|
+
usedTokens: number;
|
|
83
|
+
allowanceTokens: number;
|
|
84
|
+
windowTokens: number | null;
|
|
85
|
+
headerBoundTokens: number | null;
|
|
86
|
+
};
|
|
87
|
+
reason?: string;
|
|
88
|
+
}
|
|
89
|
+
export declare class Refusal extends Error {
|
|
90
|
+
readonly result: ContinueResult;
|
|
91
|
+
constructor(result: ContinueResult);
|
|
92
|
+
}
|
|
93
|
+
export interface NoticeEntry {
|
|
94
|
+
number: number;
|
|
95
|
+
path: string;
|
|
96
|
+
title: string;
|
|
97
|
+
summary: string;
|
|
98
|
+
status: 'ok' | 'modified' | 'missing';
|
|
99
|
+
/** user+assistant message count (record §7.3); absent for legacy records. */
|
|
100
|
+
messages?: number;
|
|
101
|
+
}
|
|
102
|
+
/** The child's whole content: honest preamble, in-flight note, flat index, back-links. */
|
|
103
|
+
export declare function assembleNotice(input: {
|
|
104
|
+
title: string;
|
|
105
|
+
handoffNote: string;
|
|
106
|
+
entries: readonly NoticeEntry[];
|
|
107
|
+
rootSession: string;
|
|
108
|
+
parentSession: string;
|
|
109
|
+
storeRoot: string;
|
|
110
|
+
projectLine?: string;
|
|
111
|
+
/** P3 §7.3: CORE RULES + category index, composed by the adapter from this
|
|
112
|
+
* machine's own approvals. Absent/empty => zero injected bytes (tape-safe). */
|
|
113
|
+
rulesSection?: string;
|
|
114
|
+
}): string;
|
|
115
|
+
/** The budget formula with its refusal numbers, kept apart for direct testing. */
|
|
116
|
+
export declare function preflight(input: {
|
|
117
|
+
noticeTokens: number;
|
|
118
|
+
budget: BudgetProbe;
|
|
119
|
+
ratio: number;
|
|
120
|
+
}): {
|
|
121
|
+
ok: true;
|
|
122
|
+
budget: NonNullable<ContinueResult['budget']>;
|
|
123
|
+
} | {
|
|
124
|
+
ok: false;
|
|
125
|
+
budget: NonNullable<ContinueResult['budget']>;
|
|
126
|
+
};
|
|
127
|
+
export declare function runContinue(ports: ContinuePorts, args: ContinueArgs, config: ContinueConfig): Promise<ContinueResult>;
|
|
128
|
+
/** Fields both continue and fork need to create the child. */
|
|
129
|
+
export type FinishArgs = Pick<ContinueArgs, 'callerSessionId' | 'callerPreset' | 'title' | 'handoffNote' | 'projectLine' | 'rulesSection'>;
|
|
130
|
+
/**
|
|
131
|
+
* chapters_fork: open a sibling line citing the SAME archive, archiving nothing
|
|
132
|
+
* new. The point is branching (AGENTS "branchable — history can fork, not
|
|
133
|
+
* just truncate"): parent and child keep working independently; no files are
|
|
134
|
+
* written and no numbers reserved, so a fork is cheap and cannot orphan a
|
|
135
|
+
* citation it did not already exist.
|
|
136
|
+
*/
|
|
137
|
+
export interface ForkArgs extends FinishArgs {
|
|
138
|
+
}
|
|
139
|
+
export declare function runFork(ports: ContinuePorts, args: ForkArgs, config: ContinueConfig): Promise<ContinueResult>;
|
|
140
|
+
/**
|
|
141
|
+
* Deterministic segmentation: split [0..anchorSeq] into chapter ranges WITHOUT
|
|
142
|
+
* any model call — the fork-button backend (a button cannot wait for proposed
|
|
143
|
+
* ranges, and auto mode must never paraphrase: invariant 4 holds, only
|
|
144
|
+
* positions are computed here; titles/summaries derive from content lines).
|
|
145
|
+
* Cuts land ONLY on `turn/end` boundaries; a segment closes at the first
|
|
146
|
+
* boundary reaching ~90% of chapterTokenTarget, so overshoot is bounded.
|
|
147
|
+
*/
|
|
148
|
+
export declare function deriveRanges(events: readonly SessionEventLike[], anchorSeq: number, chapterTokenTarget: number, fromSeq?: number): {
|
|
149
|
+
chapters: ChapterRange[];
|
|
150
|
+
notes: string[];
|
|
151
|
+
};
|
|
152
|
+
/** The adapter surfaces refusals as tool output, never as crashes. */
|
|
153
|
+
export declare function refusalResult(error: unknown): ContinueResult | null;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { chapterTopics } from "./signature.js";
|
|
2
|
+
import { estimateTokens, renderChapter, validateRanges } from "./render.js";
|
|
3
|
+
import { attemptKey, coverage, verifyChapter, writeArchive } from "./archive.js";
|
|
4
|
+
import { appendChapters, freshSession, linkChild, reserve } from "./registry.js";
|
|
5
|
+
import { buildTocNotice } from "./notice.js";
|
|
6
|
+
import { deriveIdentity } from "./engine-core.js";
|
|
7
|
+
export class Refusal extends Error {
|
|
8
|
+
result;
|
|
9
|
+
constructor(result) {
|
|
10
|
+
super(result.reason ?? 'refused');
|
|
11
|
+
this.result = result;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** The child's whole content: honest preamble, in-flight note, flat index, back-links. */
|
|
15
|
+
export function assembleNotice(input) {
|
|
16
|
+
const lines = [
|
|
17
|
+
`# Continuation: ${input.title}`,
|
|
18
|
+
...(input.projectLine !== undefined && input.projectLine !== '' ? ['', input.projectLine] : []),
|
|
19
|
+
...(input.rulesSection !== undefined && input.rulesSection !== '' ? ['', input.rulesSection] : []),
|
|
20
|
+
'',
|
|
21
|
+
'This session continues an archived conversation. Chapters are verbatim Markdown in the workspace;',
|
|
22
|
+
'every byte remains retrievable — inline, or at artifact paths cited inside a chapter. Read a chapter',
|
|
23
|
+
'by its path with the read tool when the detail matters. The previous session stays intact.',
|
|
24
|
+
'',
|
|
25
|
+
'## In flight',
|
|
26
|
+
input.handoffNote.trim().length > 0 ? input.handoffNote.trim() : '(none stated)',
|
|
27
|
+
'',
|
|
28
|
+
'## Chapters',
|
|
29
|
+
];
|
|
30
|
+
for (const e of input.entries) {
|
|
31
|
+
const mark = e.status === 'ok' ? '' : ` ⚠ ${e.status} since archived`;
|
|
32
|
+
const msgs = e.messages !== undefined ? ` (${e.messages} msgs)` : '';
|
|
33
|
+
lines.push(`${e.number}. [${e.title}](${e.path}) — ${e.summary}${msgs}${mark}`);
|
|
34
|
+
}
|
|
35
|
+
lines.push('', `Ancestry: root ${input.rootSession}; parent ${input.parentSession}; archive under ${input.storeRoot}/.`);
|
|
36
|
+
return lines.join('\n');
|
|
37
|
+
}
|
|
38
|
+
/** The budget formula with its refusal numbers, kept apart for direct testing. */
|
|
39
|
+
export function preflight(input) {
|
|
40
|
+
const window = input.budget.windowTokens;
|
|
41
|
+
if (window === null) {
|
|
42
|
+
const budget = { usedTokens: input.noticeTokens, allowanceTokens: 0, windowTokens: null, headerBoundTokens: input.budget.headerBoundTokens };
|
|
43
|
+
return { ok: false, budget };
|
|
44
|
+
}
|
|
45
|
+
const remaining = Math.max(0, window - (input.budget.headerBoundTokens ?? 0));
|
|
46
|
+
const allowance = Math.floor(remaining * input.ratio);
|
|
47
|
+
const budget = { usedTokens: input.noticeTokens, allowanceTokens: allowance, windowTokens: window, headerBoundTokens: input.budget.headerBoundTokens };
|
|
48
|
+
return input.noticeTokens <= allowance ? { ok: true, budget } : { ok: false, budget };
|
|
49
|
+
}
|
|
50
|
+
/** Ancestor ids root-first, via registry links; cycles refuse, never hang. */
|
|
51
|
+
async function ancestryPath(ports, sessionId, limit = 1000) {
|
|
52
|
+
const walk = [];
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
let cursor = sessionId;
|
|
55
|
+
while (cursor !== null) {
|
|
56
|
+
if (seen.has(cursor) || walk.length >= limit)
|
|
57
|
+
throw new Refusal({ ok: false, reason: `registry ancestry cycle/limit at ${cursor}` });
|
|
58
|
+
seen.add(cursor);
|
|
59
|
+
walk.unshift(cursor);
|
|
60
|
+
cursor = (await ports.getState(cursor)).parentSession;
|
|
61
|
+
}
|
|
62
|
+
return walk;
|
|
63
|
+
}
|
|
64
|
+
export async function runContinue(ports, args, config) {
|
|
65
|
+
// ---- 1. Facts: ceiling and ranges.
|
|
66
|
+
const allEvents = await ports.readCallerEvents();
|
|
67
|
+
const lastTurnEnd = [...allEvents].reverse().find((e) => e.type === 'turn/end');
|
|
68
|
+
if (lastTurnEnd === undefined) {
|
|
69
|
+
throw new Refusal({ ok: false, reason: 'no completed turn to archive — the conversation has not reached a turn/end boundary' });
|
|
70
|
+
}
|
|
71
|
+
const ceiling = lastTurnEnd.seq;
|
|
72
|
+
try {
|
|
73
|
+
validateRanges(args.chapters, ceiling);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
throw new Refusal({ ok: false, reason: `ranges refused: ${error.message}` });
|
|
77
|
+
}
|
|
78
|
+
const below = allEvents.filter((e) => e.seq <= ceiling);
|
|
79
|
+
const gaps = coverage(args.chapters, ceiling).gaps;
|
|
80
|
+
const warnings = gaps.map((g) => `seqs ${g.from}-${g.to} fall outside the supplied ranges — NOT archived (gaps are reported, never silent)`);
|
|
81
|
+
// ---- 2. Render + reserve + write (idempotent per attempt key).
|
|
82
|
+
const callerState = await ports.getState(args.callerSessionId);
|
|
83
|
+
const renderConfig = {
|
|
84
|
+
toolResultDeferFloorTokens: config.toolResultDeferFloorTokens,
|
|
85
|
+
chapterTokenTarget: config.chapterTokenTarget,
|
|
86
|
+
};
|
|
87
|
+
const rendered = args.chapters.map((range) => renderChapter(below, range, renderConfig, args.toolResultOverrides, chapterTopics(below, range.startSeq, range.endSeq)));
|
|
88
|
+
warnings.push(...rendered
|
|
89
|
+
.filter((r) => r.stats.overTarget)
|
|
90
|
+
.map((r) => `chapter "${r.range.title}" is ${r.stats.estimatedTokens} est tokens over chapterTokenTarget — split at the next continuation, never clip`));
|
|
91
|
+
const fs = ports.fs();
|
|
92
|
+
const wrote = await writeArchive({
|
|
93
|
+
fs,
|
|
94
|
+
allocator: {
|
|
95
|
+
async reserve(attemptId, count) {
|
|
96
|
+
const fresh = await ports.getState(args.callerSessionId);
|
|
97
|
+
const r = reserve(fresh, attemptId, count);
|
|
98
|
+
await ports.putState(args.callerSessionId, r.state);
|
|
99
|
+
return r.numbers;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
storeRoot: config.artifactStoreRoot,
|
|
103
|
+
rootSessionId: callerState.rootSession,
|
|
104
|
+
chapters: rendered,
|
|
105
|
+
attemptId: attemptKey(args.callerSessionId, ceiling),
|
|
106
|
+
});
|
|
107
|
+
warnings.push(...wrote.warnings);
|
|
108
|
+
// ---- 3. Cumulative flat index over the ancestor path, integrity-verified.
|
|
109
|
+
const entries = await collectEntries(ports, args.callerSessionId, wrote.records, warnings);
|
|
110
|
+
// ---- 4/5. Notice + budget gate, create the child, commit.
|
|
111
|
+
return await finishChild(ports, config, args, callerState, entries, warnings, wrote.records);
|
|
112
|
+
}
|
|
113
|
+
/** Ancestors' committed records (root-first) plus fresh records; verified, numbered for display. */
|
|
114
|
+
async function collectEntries(ports, callerSessionId, freshRecords, warnings) {
|
|
115
|
+
const fs = ports.fs();
|
|
116
|
+
const pathIds = await ancestryPath(ports, callerSessionId);
|
|
117
|
+
const priorRecords = [];
|
|
118
|
+
for (const id of pathIds) {
|
|
119
|
+
const st = await ports.getState(id);
|
|
120
|
+
// Registry order is insertion order = authoring order within a session;
|
|
121
|
+
// sessions walk root-first, so reading order IS causal order.
|
|
122
|
+
priorRecords.push(...st.chapters);
|
|
123
|
+
}
|
|
124
|
+
const seenPaths = new Set();
|
|
125
|
+
const cited = [];
|
|
126
|
+
for (const record of [...priorRecords, ...freshRecords]) {
|
|
127
|
+
if (seenPaths.has(record.path))
|
|
128
|
+
continue; // a retry re-wrote the same path: cite once
|
|
129
|
+
seenPaths.add(record.path);
|
|
130
|
+
cited.push(record);
|
|
131
|
+
}
|
|
132
|
+
const entries = [];
|
|
133
|
+
for (const record of cited) {
|
|
134
|
+
const status = await verifyChapter(fs, record);
|
|
135
|
+
if (status !== 'ok')
|
|
136
|
+
warnings.push(`${record.path}: ${status} since archived — marked in the TOC, not silently trusted`);
|
|
137
|
+
entries.push({ number: entries.length + 1, path: record.path, title: record.title, summary: record.summary, status, ...(record.messages !== undefined ? { messages: record.messages } : {}) });
|
|
138
|
+
}
|
|
139
|
+
return entries;
|
|
140
|
+
}
|
|
141
|
+
/** Shared tail of both flows: notice -> budget gate -> create (invariant 2) -> commit. */
|
|
142
|
+
async function finishChild(ports, config, args, callerState, entries, warnings, commitRecords) {
|
|
143
|
+
// ---- Notice text, then the budget gate (measure the WHOLE notice).
|
|
144
|
+
const noticeText = assembleNotice({
|
|
145
|
+
title: args.title,
|
|
146
|
+
handoffNote: args.handoffNote,
|
|
147
|
+
entries,
|
|
148
|
+
rootSession: callerState.rootSession,
|
|
149
|
+
parentSession: args.callerSessionId,
|
|
150
|
+
storeRoot: config.artifactStoreRoot,
|
|
151
|
+
...(args.projectLine !== undefined ? { projectLine: args.projectLine } : {}),
|
|
152
|
+
...(args.rulesSection !== undefined ? { rulesSection: args.rulesSection } : {}),
|
|
153
|
+
});
|
|
154
|
+
const budget = preflight({
|
|
155
|
+
noticeTokens: estimateTokens(noticeText),
|
|
156
|
+
budget: await ports.budgetProbe(),
|
|
157
|
+
ratio: config.continuationBudgetRatio,
|
|
158
|
+
});
|
|
159
|
+
if (!budget.ok) {
|
|
160
|
+
throw new Refusal({
|
|
161
|
+
ok: false,
|
|
162
|
+
reason: budget.budget.windowTokens === null
|
|
163
|
+
? 'cannot resolve the model context window for this caller — refusing to create an unbounded continuation (check agentDefaultModel selection / adapter metadata)'
|
|
164
|
+
: `continuation over budget: notice ~${budget.budget.usedTokens} tokens > allowance ${budget.budget.allowanceTokens} (${config.continuationBudgetRatio} × (window ${budget.budget.windowTokens} − header bound ${budget.budget.headerBoundTokens ?? 'unmeasured'})). Shorten the handoff note or split the archive.`,
|
|
165
|
+
budget: budget.budget,
|
|
166
|
+
warnings,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// ---- Create the child (one synthetic event at seq 0 — invariant 2), then commit.
|
|
170
|
+
const noticeEvent = buildTocNotice(noticeText, { newId: ports.newId, time: ports.now() });
|
|
171
|
+
const childId = `ch-${ports.newId()}`;
|
|
172
|
+
const presetId = args.callerPreset ?? config.fallbackPreset;
|
|
173
|
+
try {
|
|
174
|
+
await ports.createChild({ sessionId: childId, noticeEvent, presetId, title: args.title });
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
throw new Refusal({ ok: false, reason: `child creation refused: ${error.message}`, warnings });
|
|
178
|
+
}
|
|
179
|
+
if (commitRecords.length > 0) {
|
|
180
|
+
const callerFresh = await ports.getState(args.callerSessionId);
|
|
181
|
+
await ports.putState(args.callerSessionId, appendChapters(callerFresh, commitRecords));
|
|
182
|
+
}
|
|
183
|
+
await ports.putState(childId, linkChild(freshSession(childId), args.callerSessionId, callerState.rootSession));
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
childSessionId: childId,
|
|
187
|
+
presetUsed: presetId,
|
|
188
|
+
chapters: commitRecords.map((r) => ({ number: r.number, path: r.path, startSeq: r.startSeq, endSeq: r.endSeq })),
|
|
189
|
+
warnings,
|
|
190
|
+
budget: budget.budget,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
export async function runFork(ports, args, config) {
|
|
194
|
+
const warnings = [];
|
|
195
|
+
const callerState = await ports.getState(args.callerSessionId);
|
|
196
|
+
const entries = await collectEntries(ports, args.callerSessionId, [], warnings);
|
|
197
|
+
return await finishChild(ports, config, args, callerState, entries, warnings, []);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Deterministic segmentation: split [0..anchorSeq] into chapter ranges WITHOUT
|
|
201
|
+
* any model call — the fork-button backend (a button cannot wait for proposed
|
|
202
|
+
* ranges, and auto mode must never paraphrase: invariant 4 holds, only
|
|
203
|
+
* positions are computed here; titles/summaries derive from content lines).
|
|
204
|
+
* Cuts land ONLY on `turn/end` boundaries; a segment closes at the first
|
|
205
|
+
* boundary reaching ~90% of chapterTokenTarget, so overshoot is bounded.
|
|
206
|
+
*/
|
|
207
|
+
export function deriveRanges(events, anchorSeq, chapterTokenTarget, fromSeq = 0) {
|
|
208
|
+
const upto = events.filter((e) => e.seq <= anchorSeq);
|
|
209
|
+
const boundaries = upto.filter((e) => e.type === 'turn/end' && e.seq >= fromSeq).map((e) => e.seq);
|
|
210
|
+
if (boundaries.length === 0) {
|
|
211
|
+
throw new Refusal({ ok: false, reason: fromSeq > 0
|
|
212
|
+
? `nothing new to archive: every completed turn at or before seq ${anchorSeq} is already in the archive (watermark ${fromSeq - 1})`
|
|
213
|
+
: `no completed turn at or before seq ${anchorSeq} — nothing to branch from yet` });
|
|
214
|
+
}
|
|
215
|
+
const last = boundaries[boundaries.length - 1];
|
|
216
|
+
const notes = [];
|
|
217
|
+
if (last !== anchorSeq) {
|
|
218
|
+
notes.push(`anchor ${anchorSeq} is not a turn boundary: segmentation ran to ${last}; later events belong to an unfinished turn (describe them in the handoff note)`);
|
|
219
|
+
}
|
|
220
|
+
const tokensBetween = (lo, hi) => upto
|
|
221
|
+
.filter((e) => e.seq >= lo && e.seq <= hi)
|
|
222
|
+
.reduce((n, e) => n + Math.ceil(JSON.stringify(e.data ?? {}).length / 4), 0);
|
|
223
|
+
const chapters = [];
|
|
224
|
+
const cut = Math.max(1, Math.floor(chapterTokenTarget * 0.9));
|
|
225
|
+
let start = fromSeq;
|
|
226
|
+
for (const b of boundaries) {
|
|
227
|
+
const isLast = b === last;
|
|
228
|
+
if (isLast || tokensBetween(start, b) >= cut) {
|
|
229
|
+
const seg = upto.filter((e) => e.seq >= start && e.seq <= b);
|
|
230
|
+
const msgs = seg
|
|
231
|
+
.filter((e) => e.type === 'user/message' || e.type === 'assistant/message')
|
|
232
|
+
.map((e) => {
|
|
233
|
+
const d = e.data;
|
|
234
|
+
return { role: e.type === 'assistant/message' ? 'assistant' : 'user', content: d?.content ?? [] };
|
|
235
|
+
});
|
|
236
|
+
const identity = deriveIdentity(msgs, seg.some((e) => JSON.stringify(e.data ?? '').includes('<compacted-summary>')));
|
|
237
|
+
chapters.push({
|
|
238
|
+
title: `${identity.title} (${chapters.length + 1})`,
|
|
239
|
+
summary: `events ${start}-${b}; ${identity.summary}`,
|
|
240
|
+
startSeq: start,
|
|
241
|
+
endSeq: b,
|
|
242
|
+
});
|
|
243
|
+
start = b + 1;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return { chapters, notes };
|
|
247
|
+
}
|
|
248
|
+
/** The adapter surfaces refusals as tool output, never as crashes. */
|
|
249
|
+
export function refusalResult(error) {
|
|
250
|
+
return error instanceof Refusal ? error.result : null;
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=continue-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"continue-core.js","sourceRoot":"","sources":["../src/continue-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine core — the deterministic logic behind ChaptersCompactionEngine, as
|
|
3
|
+
* pure functions over minimal structural types. NO cordis, NO @deepseek-ai
|
|
4
|
+
* imports: this file runs under `node --test` with no harness, which is where
|
|
5
|
+
* the two scariest engine-path bugs get caught — a lost TOC bullet (silently
|
|
6
|
+
* unreachable chapter) and a mis-resolved citation (wrong text archived).
|
|
7
|
+
*
|
|
8
|
+
* The correlation model (docs/host-compaction-seam.md + FINDINGS § Phase 0b):
|
|
9
|
+
* summarize() — sees region CONTENT (no seqs); reserves numbers keyed by the
|
|
10
|
+
* OPEN transaction's compactionId (found by scanning the log:
|
|
11
|
+
* the last `compaction/start` without a matching `end` — the
|
|
12
|
+
* durable lock guarantees uniqueness) and emits the TOC text
|
|
13
|
+
* citing deterministic paths.
|
|
14
|
+
* finalize() — post-commit, from authoritative CompactionResult.shadowedSeqs,
|
|
15
|
+
* renders bodies from the DURABLE LOG, following each
|
|
16
|
+
* `tool/result` replacement's `sourceEventSeqs` chain back to
|
|
17
|
+
* the pre-prune original. Never from summarize's input.
|
|
18
|
+
*
|
|
19
|
+
* Checkpoint merge-forward: the next compaction's span usually includes the
|
|
20
|
+
* previous checkpoint node (r12 measured it). Its TOC bullets are extracted
|
|
21
|
+
* and re-emitted; an embedded LLM-style summary (a host `basic` checkpoint
|
|
22
|
+
* from before this engine was mounted) is carried verbatim ONCE — honest,
|
|
23
|
+
* bounded, and never silently dropped.
|
|
24
|
+
*/
|
|
25
|
+
import type { ChapterRange, RenderedChapter } from './types.ts';
|
|
26
|
+
import type { ChapterRecord } from './archive.ts';
|
|
27
|
+
import type { PlanChapter, SessionState } from './registry.ts';
|
|
28
|
+
/** provider tag written into `compaction/summary` — our event-scan discriminator. */
|
|
29
|
+
export declare const CHAPTERS_PROVIDER = "dsh-chapters";
|
|
30
|
+
export declare const DETERMINISTIC_MODEL = "deterministic";
|
|
31
|
+
/** Framing tags the host's summarizer wraps checkpoints in (compaction-basic summarizer.ts:21-22). */
|
|
32
|
+
export declare const SUMMARY_OPEN_TAG = "<compacted-summary>";
|
|
33
|
+
export declare const SUMMARY_CLOSE_TAG = "</compacted-summary>";
|
|
34
|
+
/** One TOC bullet, as re-parsed from prior checkpoints — the accumulation unit. */
|
|
35
|
+
export interface TocBullet {
|
|
36
|
+
title: string;
|
|
37
|
+
path: string;
|
|
38
|
+
summary: string;
|
|
39
|
+
}
|
|
40
|
+
export interface EngineConfig {
|
|
41
|
+
/** Workspace-relative archive root; must be reachable by the `read` tool. */
|
|
42
|
+
artifactStoreRoot: string;
|
|
43
|
+
chapterTokenTarget: number;
|
|
44
|
+
toolResultDeferFloorTokens: number;
|
|
45
|
+
/** Composer tunables (record §4.2/§12) — same register, same defaults as the host plugin's. */
|
|
46
|
+
mergeThreshold: number;
|
|
47
|
+
chapterLimit: number;
|
|
48
|
+
/** Arrival-time artifacting floor (architecture.md amendment 2026-09-19):
|
|
49
|
+
* tool results at/above this many estimated tokens are stored as artifacts
|
|
50
|
+
* and replaced by reference stubs BEFORE the next request is composed. */
|
|
51
|
+
toolResultArtifactTokens: number;
|
|
52
|
+
/** Elicit a plot note with one bounded call when the model wrote none. */
|
|
53
|
+
elicitedPlot: boolean;
|
|
54
|
+
}
|
|
55
|
+
export declare const ENGINE_CONFIG_DEFAULTS: EngineConfig;
|
|
56
|
+
export interface SummarizeInputLike {
|
|
57
|
+
readonly tools?: readonly unknown[];
|
|
58
|
+
readonly messages: readonly EngineMessage[];
|
|
59
|
+
}
|
|
60
|
+
export interface EngineMessage {
|
|
61
|
+
readonly role: string;
|
|
62
|
+
readonly content?: readonly {
|
|
63
|
+
type: string;
|
|
64
|
+
text?: string;
|
|
65
|
+
}[];
|
|
66
|
+
}
|
|
67
|
+
export interface SummarizeResultLike {
|
|
68
|
+
summary: {
|
|
69
|
+
type: 'text';
|
|
70
|
+
text: string;
|
|
71
|
+
}[];
|
|
72
|
+
provider: string;
|
|
73
|
+
model: string;
|
|
74
|
+
}
|
|
75
|
+
/** The slice of `Session` the core reads. Kept small so fakes are honest. */
|
|
76
|
+
export interface EngineSession {
|
|
77
|
+
readonly id: string;
|
|
78
|
+
readonly seq: number;
|
|
79
|
+
eventAt(seq: number): EngineSessionEvent | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Present on the real host Session (a class) — `reconstructShadowedSeqs`
|
|
82
|
+
* needs them to map summarize-input messages back to log seqs. Absence of
|
|
83
|
+
* either simply means: no reconstruction, legacy single-chapter compaction.
|
|
84
|
+
*/
|
|
85
|
+
readonly surface?: {
|
|
86
|
+
readonly nodes?: readonly {
|
|
87
|
+
readonly seq: number;
|
|
88
|
+
}[];
|
|
89
|
+
};
|
|
90
|
+
deriveEventMessage?(event: EngineSessionEvent): EngineMessage | null | undefined;
|
|
91
|
+
}
|
|
92
|
+
export interface EngineSessionEvent {
|
|
93
|
+
readonly type: string;
|
|
94
|
+
readonly seq: number;
|
|
95
|
+
readonly time?: number;
|
|
96
|
+
readonly sourceEventSeqs?: readonly number[];
|
|
97
|
+
readonly data?: Record<string, unknown>;
|
|
98
|
+
}
|
|
99
|
+
/** Every `<compacted-summary>…</compacted-summary>` block in the region, in order. */
|
|
100
|
+
export declare function extractCheckpointBlocks(messages: readonly EngineMessage[]): string[];
|
|
101
|
+
/**
|
|
102
|
+
* Merge-forward: parse prior bullets out of every checkpoint block. Lines that
|
|
103
|
+
* are not our bullet format (e.g. an LLM summary produced by `basic` before
|
|
104
|
+
* this engine took over) are returned as verbatim carry-overs — dropping them
|
|
105
|
+
* would be exactly the loss this plugin exists to prevent.
|
|
106
|
+
*/
|
|
107
|
+
export declare function parseTocState(blocks: readonly string[]): {
|
|
108
|
+
bullets: TocBullet[];
|
|
109
|
+
carriedProse: string[];
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* The open transaction's compactionId: the latest `compaction/start` with no
|
|
113
|
+
* later `compaction/end` carrying the same id. The base appends the start
|
|
114
|
+
* BEFORE awaiting summarize (region.ts:211) and the durable lock admits only
|
|
115
|
+
* one open transaction per session — so a backward scan finds exactly it.
|
|
116
|
+
*/
|
|
117
|
+
export declare function findOpenCompactionId(session: EngineSession): string | null;
|
|
118
|
+
/** All OUR finalized-or-not summary events (provider-tagged), oldest first. */
|
|
119
|
+
export declare function scanChaptersSummaries(session: EngineSession): Array<{
|
|
120
|
+
compactionId: string;
|
|
121
|
+
summarySeq: number;
|
|
122
|
+
shadowedSeqs: number[];
|
|
123
|
+
}>;
|
|
124
|
+
/**
|
|
125
|
+
* Follow `tool/result` replacement chains to the first version (the un-pruned
|
|
126
|
+
* original). Only single-cited tool results qualify — a compaction checkpoint
|
|
127
|
+
* `user/message` also carries sourceEventSeqs, and following THOSE would pull
|
|
128
|
+
* lifecycle bookkeeping into chapter bodies.
|
|
129
|
+
*/
|
|
130
|
+
export declare function resolveOriginalEvent(session: EngineSession, seq: number): EngineSessionEvent | undefined;
|
|
131
|
+
export interface SummarizePlan {
|
|
132
|
+
/** TOC text for the checkpoint (what summarize returns). */
|
|
133
|
+
tocText: string;
|
|
134
|
+
/** Chapter numbers reserved during planning — attemptId `compaction:${id}`. */
|
|
135
|
+
numbers: number[];
|
|
136
|
+
/** Everything finalize needs, since finalize cannot re-derive the title. */
|
|
137
|
+
chapter: {
|
|
138
|
+
title: string;
|
|
139
|
+
summary: string;
|
|
140
|
+
path: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Topic-composed plan (r29): when the composer ran over the span, one
|
|
144
|
+
* manifest chapter per composed range — finalize renders exactly these,
|
|
145
|
+
* it never recomposes (the TOC cited them; determinism is the contract).
|
|
146
|
+
*/
|
|
147
|
+
chapters?: PlanChapter[];
|
|
148
|
+
}
|
|
149
|
+
/** Deterministic title/summary from region content. Never model-authored (invariant 4 — the model is absent). */
|
|
150
|
+
export declare function deriveIdentity(messages: readonly EngineMessage[], hasCheckpoint: boolean): {
|
|
151
|
+
title: string;
|
|
152
|
+
summary: string;
|
|
153
|
+
};
|
|
154
|
+
/** Workspace-relative chapter path for a reserved number. */
|
|
155
|
+
export declare const chapterPathFor: (config: EngineConfig, rootSession: string, number: number, title: string) => string;
|
|
156
|
+
/**
|
|
157
|
+
* Map the prepared summarize input back to the log seqs it was built from
|
|
158
|
+
* (the host builds region messages as `deriveEventMessage(eventAt(seq))` for
|
|
159
|
+
* the shadowed selection). Returns null — meaning "use the legacy
|
|
160
|
+
* single-chapter path" — whenever the Session lacks the mapping API, nothing
|
|
161
|
+
* matches, or MORE THAN ONE contiguous run matches (duplicate small
|
|
162
|
+
* messages are common; guessing a wrong span would cite chapters that never
|
|
163
|
+
* cover their seqs, and an honest legacy chapter beats that).
|
|
164
|
+
*/
|
|
165
|
+
export declare function reconstructShadowedSeqs(session: EngineSession, input: SummarizeInputLike): number[] | null;
|
|
166
|
+
/**
|
|
167
|
+
* The summarize-time plan: merge prior bullets, derive this span's identity,
|
|
168
|
+
* reserve numbers (attempt-keyed by the OPEN transaction's compactionId),
|
|
169
|
+
* build the TOC. Pure given the state; the caller persists the returned
|
|
170
|
+
* state change BEFORE this text can ever be cited.
|
|
171
|
+
*
|
|
172
|
+
* `composition` (r29): topic-sequential ranges from the composer (record
|
|
173
|
+
* §4.2), spanning the whole selected region head-to-tail (the adapter
|
|
174
|
+
* absorbs any strays). Present => reserve one number per chapter and cite
|
|
175
|
+
* them all; absent => the legacy single-chapter identity path, unchanged.
|
|
176
|
+
*/
|
|
177
|
+
export declare function planSummarize(session: EngineSession, state: SessionState, input: SummarizeInputLike, config: EngineConfig, reserveFn: (state: SessionState, attemptId: string, count: number) => {
|
|
178
|
+
state: SessionState;
|
|
179
|
+
numbers: number[];
|
|
180
|
+
},
|
|
181
|
+
/** Required: the caller fails loudly when no `compaction/start` is open. */
|
|
182
|
+
compactionId: string, composition?: readonly ChapterRange[] | null,
|
|
183
|
+
/** Model-authored plot note carried into this checkpoint (may be null). */
|
|
184
|
+
plot?: string | null): {
|
|
185
|
+
plan: SummarizePlan;
|
|
186
|
+
state: SessionState;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* The plot note (architecture.md amendment 2026-09-19): a short forward-
|
|
190
|
+
* maintained statement of what the agent is MID-WAY through, authored by the
|
|
191
|
+
* model itself and carried forward across every in-place checkpoint.
|
|
192
|
+
*/
|
|
193
|
+
export declare const PLOT_MARKER = "PLOT:";
|
|
194
|
+
/**
|
|
195
|
+
* Latest plot paragraph in the region about to be shadowed. Assistant text
|
|
196
|
+
* wins; a PLOT carried in an earlier checkpoint (user-role, plugin-sourced)
|
|
197
|
+
* is the fallback, so the thread survives repeatedly shadowed chains.
|
|
198
|
+
* Returns the paragraph text WITHOUT the marker line, capped at maxChars.
|
|
199
|
+
*/
|
|
200
|
+
export declare function extractPlot(messages: readonly EngineMessage[], maxChars?: number): string | null;
|
|
201
|
+
/**
|
|
202
|
+
* Post-commit: render the chapter body for one compaction from the durable
|
|
203
|
+
* log (citation-resolved), and return the record to commit. The caller writes
|
|
204
|
+
* files (via archive.writeArchive) and persists state; this stays pure over
|
|
205
|
+
* the event data so the pruner-composition rule is testable.
|
|
206
|
+
*/
|
|
207
|
+
export declare function buildFinalizedChapter(session: EngineSession, shadowedSeqs: readonly number[], plan: SummarizePlan, config: EngineConfig): RenderedChapter;
|
|
208
|
+
/**
|
|
209
|
+
* Render the manifest's chapters (r29) — authoritative ranges, no
|
|
210
|
+
* recomposition (the TOC cited them). A plan without composed chapters
|
|
211
|
+
* returns the legacy single render. Mismatch between manifest and the
|
|
212
|
+
* committed shadowed span is a LOUD error: the finalizer defers and warns,
|
|
213
|
+
* never silently archives the wrong content.
|
|
214
|
+
*/
|
|
215
|
+
export declare function buildFinalizedChapters(session: EngineSession, shadowedSeqs: readonly number[], plan: SummarizePlan, config: EngineConfig): RenderedChapter[];
|
|
216
|
+
/** The registry record shape written post-writeArchive for an engine chapter. */
|
|
217
|
+
export declare function engineRecord(plan: SummarizePlan, shadowedSeqs: readonly number[], rendered: RenderedChapter, writtenPath: string, sha256hex: string): ChapterRecord;
|
|
218
|
+
/** Numbers the plan reserved, or null when the manifest disagrees (corruption). */
|
|
219
|
+
export declare function assertReservationMatchesPlan(state: SessionState, compactionId: string, plan: SummarizePlan): boolean;
|