@treeseed/dsh-chapters 0.1.24 → 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 +49 -1
- package/docs/architecture.md +1 -496
- 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 +74 -0
- package/lib/auto-save.js +162 -26
- package/lib/auto-save.js.map +1 -1
- package/lib/client.js +413 -11
- package/lib/client.js.map +1 -1
- package/lib/commands.d.ts +8 -1
- package/lib/commands.js +135 -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 +63 -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 +51 -0
- package/lib/index.js +180 -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 +75 -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 +31 -1
- package/lib/save-core.js +153 -17
- package/lib/save-core.js.map +1 -1
- 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 +311 -32
- package/lib/tools.js.map +1 -1
- package/lib/types.d.ts +12 -1
- 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/archive.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface ArchiveFs {
|
|
|
17
17
|
}
|
|
18
18
|
/** Number allocation through the registry, never by scanning a directory (races a concurrent sibling). */
|
|
19
19
|
export interface NumberAllocator {
|
|
20
|
+
/** Free an attempt key when NOTHING landed, so a failed door cannot leave keys accumulating. */
|
|
21
|
+
release?(attemptId: string): Promise<void>;
|
|
20
22
|
reserve(key: string, count: number): Promise<number[]>;
|
|
21
23
|
/**
|
|
22
24
|
* Separate allocation for plan DOCUMENTS (docs/plans-carriage.md § 11), so a plan
|
|
@@ -69,6 +71,20 @@ export interface ChapterRecord {
|
|
|
69
71
|
path: string;
|
|
70
72
|
sha256: string;
|
|
71
73
|
}> | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Set by repair (#60): this record was superseded by chapter `supersededBy`. A superseded chapter is
|
|
76
|
+
* NOT knowledge — no TOC bullet, no enrichment pass, no search entry — but its record, its number and
|
|
77
|
+
* its seqs stay in the registry forever: append-only has no exceptions, and dropping a record would
|
|
78
|
+
* rewind the watermark (`max(endSeq)+1`) and let a spent number be re-spent (#59).
|
|
79
|
+
*/
|
|
80
|
+
status?: 'superseded' | undefined;
|
|
81
|
+
supersededBy?: number | undefined;
|
|
82
|
+
/** Why this chapter exists as a replacement, and which number it replaced (`{class, over, at}`). */
|
|
83
|
+
repairs?: {
|
|
84
|
+
class: string;
|
|
85
|
+
over: number;
|
|
86
|
+
at: number;
|
|
87
|
+
}[] | undefined;
|
|
72
88
|
}
|
|
73
89
|
export interface WriteResult {
|
|
74
90
|
records: ChapterRecord[];
|
|
@@ -78,14 +94,46 @@ export interface WriteResult {
|
|
|
78
94
|
}
|
|
79
95
|
/** Filesystem-safe, stable, and never empty. Title is display metadata; the number is the identity. */
|
|
80
96
|
export declare function slugify(title: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* The ONE canonical form of a stored path (#69): **workspace-relative**. `ChapterRecord.path` had two
|
|
99
|
+
* producers — the doors mint from a relative `artifactStoreRoot`, the compaction realm from a resolved
|
|
100
|
+
* ABSOLUTE one — so one file could be a cited record by one path and an "unregistered file" by the
|
|
101
|
+
* other, and `/chapters-repair` then reported its own archive as lost knowledge (measured: 6 records,
|
|
102
|
+
* chapter 1 of the two longest sessions, each `msgs > 300`, all absolute).
|
|
103
|
+
*
|
|
104
|
+
* Relative is the right winner: the store root lives inside the workspace, `read` reaches it, and a
|
|
105
|
+
* path that survives moving a checkout between machines is the same argument `projectKey` already makes
|
|
106
|
+
* for the remote over the path. An absolute path OUTSIDE the workspace is passed through unchanged — the
|
|
107
|
+
* rig legitimately pins an absolute store root per port (#61 item 8), and pretending otherwise would
|
|
108
|
+
* produce a `../../..` path that resolves nowhere.
|
|
109
|
+
*/
|
|
110
|
+
export declare function toStoredPath(cwd: string | undefined, p: string): string;
|
|
111
|
+
/**
|
|
112
|
+
* The READ direction of the stored-path dialect: turn a `ChapterRecord.path` back into something the
|
|
113
|
+
* filesystem understands. `toStoredPath` may hand back an ABSOLUTE path (the rig pins an absolute
|
|
114
|
+
* `artifactStoreRoot` per port — #61 item 8), and `path.join(cwd, absolute)` CONCATENATES instead of
|
|
115
|
+
* resolving, so a naive join sends a reader to `<cwd>/tmp/…`, which exists nowhere. Measured on the
|
|
116
|
+
* `enrich` leg 2026-10-24: the ladder reported `chapter file absent` about a file that was present,
|
|
117
|
+
* because of exactly this join.
|
|
118
|
+
*/
|
|
119
|
+
export declare function resolveStoredPath(cwd: string | undefined, stored: string): string;
|
|
120
|
+
/** Compare two stored paths in either dialect. Every path COMPARISON goes through this, never `===`. */
|
|
121
|
+
export declare function sameStoredPath(a: string, b: string, cwd?: string): boolean;
|
|
81
122
|
export declare const chapterFileName: (n: number, title: string) => string;
|
|
82
123
|
/** Idempotency key: the same source session + ceiling reuses reserved numbers instead of duplicating. */
|
|
83
|
-
|
|
124
|
+
/**
|
|
125
|
+
* The idempotence key for one archive attempt. `salt` exists for REPAIR (#60): a repair re-rendering a
|
|
126
|
+
* span the original door keyed would otherwise be swallowed as a retry of that attempt, and a swallowed
|
|
127
|
+
* repair is indistinguishable from one that worked.
|
|
128
|
+
*/
|
|
129
|
+
export declare const attemptKey: (sourceSessionId: string, archiveCeiling: number, salt?: string) => string;
|
|
84
130
|
export interface WriteInput {
|
|
85
131
|
fs: ArchiveFs;
|
|
86
132
|
allocator: NumberAllocator;
|
|
87
133
|
/** Store root, relative to the session workspace so the `read` tool can reach it. */
|
|
88
134
|
storeRoot: string;
|
|
135
|
+
/** Workspace cwd; when given, stored paths are normalized to workspace-relative (#69). */
|
|
136
|
+
cwd?: string | undefined;
|
|
89
137
|
/** Directory key — the root of the ancestry tree, not the per-fork id. One dir per tree. */
|
|
90
138
|
rootSessionId: string;
|
|
91
139
|
chapters: readonly RenderedChapter[];
|
package/lib/archive.js
CHANGED
|
@@ -4,13 +4,59 @@ export function slugify(title) {
|
|
|
4
4
|
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
5
5
|
return slug.length > 0 ? slug.slice(0, 48) : 'chapter';
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* The ONE canonical form of a stored path (#69): **workspace-relative**. `ChapterRecord.path` had two
|
|
9
|
+
* producers — the doors mint from a relative `artifactStoreRoot`, the compaction realm from a resolved
|
|
10
|
+
* ABSOLUTE one — so one file could be a cited record by one path and an "unregistered file" by the
|
|
11
|
+
* other, and `/chapters-repair` then reported its own archive as lost knowledge (measured: 6 records,
|
|
12
|
+
* chapter 1 of the two longest sessions, each `msgs > 300`, all absolute).
|
|
13
|
+
*
|
|
14
|
+
* Relative is the right winner: the store root lives inside the workspace, `read` reaches it, and a
|
|
15
|
+
* path that survives moving a checkout between machines is the same argument `projectKey` already makes
|
|
16
|
+
* for the remote over the path. An absolute path OUTSIDE the workspace is passed through unchanged — the
|
|
17
|
+
* rig legitimately pins an absolute store root per port (#61 item 8), and pretending otherwise would
|
|
18
|
+
* produce a `../../..` path that resolves nowhere.
|
|
19
|
+
*/
|
|
20
|
+
export function toStoredPath(cwd, p) {
|
|
21
|
+
if (cwd === undefined || cwd === '' || !p.startsWith('/'))
|
|
22
|
+
return p;
|
|
23
|
+
const rel = p.startsWith(cwd + '/') ? p.slice(cwd.length + 1) : null;
|
|
24
|
+
return rel ?? p;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The READ direction of the stored-path dialect: turn a `ChapterRecord.path` back into something the
|
|
28
|
+
* filesystem understands. `toStoredPath` may hand back an ABSOLUTE path (the rig pins an absolute
|
|
29
|
+
* `artifactStoreRoot` per port — #61 item 8), and `path.join(cwd, absolute)` CONCATENATES instead of
|
|
30
|
+
* resolving, so a naive join sends a reader to `<cwd>/tmp/…`, which exists nowhere. Measured on the
|
|
31
|
+
* `enrich` leg 2026-10-24: the ladder reported `chapter file absent` about a file that was present,
|
|
32
|
+
* because of exactly this join.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveStoredPath(cwd, stored) {
|
|
35
|
+
if (stored.startsWith('/') || cwd === undefined || cwd === '')
|
|
36
|
+
return stored;
|
|
37
|
+
return `${cwd.replace(/\/+$/, '')}/${stored}`;
|
|
38
|
+
}
|
|
39
|
+
/** Compare two stored paths in either dialect. Every path COMPARISON goes through this, never `===`. */
|
|
40
|
+
export function sameStoredPath(a, b, cwd) {
|
|
41
|
+
if (a === b)
|
|
42
|
+
return true;
|
|
43
|
+
const root = cwd ?? '';
|
|
44
|
+
const abs = (p) => (p.startsWith('/') ? p : root === '' ? p : `${root}/${p}`);
|
|
45
|
+
return abs(a) === abs(b);
|
|
46
|
+
}
|
|
7
47
|
export const chapterFileName = (n, title) => `${String(n).padStart(3, '0')}-${slugify(title)}.md`;
|
|
8
48
|
/** Idempotency key: the same source session + ceiling reuses reserved numbers instead of duplicating. */
|
|
9
|
-
|
|
49
|
+
/**
|
|
50
|
+
* The idempotence key for one archive attempt. `salt` exists for REPAIR (#60): a repair re-rendering a
|
|
51
|
+
* span the original door keyed would otherwise be swallowed as a retry of that attempt, and a swallowed
|
|
52
|
+
* repair is indistinguishable from one that worked.
|
|
53
|
+
*/
|
|
54
|
+
export const attemptKey = (sourceSessionId, archiveCeiling, salt) => `${sourceSessionId}@${archiveCeiling}${salt !== undefined ? `+${salt}` : ''}`;
|
|
10
55
|
/** Join without importing path, so the seam stays portable between ctx.fs and node:fs. */
|
|
11
56
|
const join = (...parts) => parts.filter(Boolean).join('/').replace(/\/{2,}/g, '/');
|
|
12
57
|
export async function writeArchive(input) {
|
|
13
58
|
const { fs, allocator, storeRoot, rootSessionId, chapters } = input;
|
|
59
|
+
const cwd = input.cwd;
|
|
14
60
|
if (chapters.length === 0)
|
|
15
61
|
throw new Error('writeArchive called with no chapters');
|
|
16
62
|
const treeDir = join(storeRoot, rootSessionId);
|
|
@@ -122,7 +168,7 @@ export async function writeArchive(input) {
|
|
|
122
168
|
}
|
|
123
169
|
records.push({
|
|
124
170
|
number,
|
|
125
|
-
path: chapterPath,
|
|
171
|
+
path: toStoredPath(cwd, chapterPath),
|
|
126
172
|
title: chapter.range.title,
|
|
127
173
|
summary: chapter.range.summary,
|
|
128
174
|
startSeq: chapter.range.startSeq,
|
|
@@ -143,6 +189,14 @@ export async function writeArchive(input) {
|
|
|
143
189
|
})(),
|
|
144
190
|
});
|
|
145
191
|
}
|
|
192
|
+
// #59: an attempt that landed NOTHING must not leave a reservation key behind forever — that is how
|
|
193
|
+
// numbers 20 and 37 stayed burned with no file and no record. PARTIAL landing keeps the key on
|
|
194
|
+
// purpose: a retry of the same attempt reuses those numbers instead of writing a second chapter
|
|
195
|
+
// over the same span.
|
|
196
|
+
if (records.length === 0 && allocator.release !== undefined) {
|
|
197
|
+
await allocator.release(input.attemptId);
|
|
198
|
+
warnings.push(`attempt ${input.attemptId} landed no chapter; its reservation key was released`);
|
|
199
|
+
}
|
|
146
200
|
return { records, artifactPathsWritten, warnings };
|
|
147
201
|
}
|
|
148
202
|
/** Coverage arithmetic: which seqs below the ceiling were NOT archived. Gaps are reported, never silent. */
|
package/lib/archive.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"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"}
|
package/lib/arrival.js
CHANGED
|
@@ -31,14 +31,25 @@ export async function applyArrivalStubs(session, opts) {
|
|
|
31
31
|
// — the text is nested one level below the wrapper. A unit fixture that
|
|
32
32
|
// invented a flat shape hid this for a whole run; the fake mirrors the
|
|
33
33
|
// real from now on.
|
|
34
|
-
const blocks = Array.isArray(data.message.content) ? data.message.content
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const blocks = Array.isArray(data.message.content) ? data.message.content
|
|
35
|
+
: typeof data.message.content === 'string' ? [{ type: 'text', text: data.message.content }] : [];
|
|
36
|
+
// TWO real shapes, one per host generation — both measured, neither assumed.
|
|
37
|
+
// 0.1.x: content = [{ type: 'tool-result', toolCallId, isError, content: [{type:'text',text}] }]
|
|
38
|
+
// 0.2.0-rc.2: content = [{ type: 'text', text }] ← flattened; measured 2026-10-22 in the
|
|
39
|
+
// arrival leg's own durable log (seq 51, 54,763 bytes; message keys role,source,toolCallId,
|
|
40
|
+
// content,isError,id; block keys type,text).
|
|
41
|
+
// Recognizing only the first meant `wrappers.length === 0` on EVERY result of the new host:
|
|
42
|
+
// nothing stubbed, `detail` unset, and no line anywhere — which is how arrival artifacting
|
|
43
|
+
// vanished from the whole acceptance chain while the engine's entry line proved it was running.
|
|
44
|
+
const wrappers = blocks.filter((b) => b?.type === 'tool-result');
|
|
45
|
+
const carriers = wrappers.length > 0 ? wrappers : blocks.filter((b) => b?.type === 'text' && typeof b.text === 'string');
|
|
46
|
+
if (carriers.length === 0)
|
|
37
47
|
continue;
|
|
38
48
|
const textOf = (w) => Array.isArray(w.content)
|
|
39
|
-
? w.content.filter((c) => c
|
|
40
|
-
: typeof w.content === 'string' ? w.content
|
|
41
|
-
|
|
49
|
+
? w.content.filter((c) => c?.type === 'text').map((c) => c.text ?? '').join('')
|
|
50
|
+
: typeof w.content === 'string' ? w.content
|
|
51
|
+
: typeof w.text === 'string' ? w.text : '';
|
|
52
|
+
const text = carriers.map(textOf).join('');
|
|
42
53
|
if (text.length === 0 || text.includes(ARRIVAL_MARKER))
|
|
43
54
|
continue;
|
|
44
55
|
const tokens = opts.estimate(data.message);
|
|
@@ -58,12 +69,15 @@ export async function applyArrivalStubs(session, opts) {
|
|
|
58
69
|
return { stubbed, detail: `artifact write failed (${String(error?.message ?? error)}); ${stubbed} node(s) stubbed before the failure` };
|
|
59
70
|
}
|
|
60
71
|
const stub = `${ARRIVAL_MARKER} ${hash.slice(0, 12)}] ${kb(text.length)} ≈ ${tokens} tok stored at ${rel} — query it with chapters_artifact (path '${rel}' or the sha): action 'toc' for the heading map with line numbers, 'search' for term hits as small blocks, 'read' for exact line ranges. The full result is NOT elsewhere in this context; re-running the original tool only recreates it.`;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
// the stub goes back in the shape it arrived: nested wrappers keep their `content` blocks,
|
|
73
|
+
// flattened v4 blocks keep their own `text` and their `type: 'text'` — a replacement that
|
|
74
|
+
// changed the block type would be a new shape assumption on the read side.
|
|
75
|
+
const stubbedCarriers = carriers.map((w, i) => (wrappers.length > 0 && Array.isArray(w.content))
|
|
76
|
+
? { ...w, content: [{ type: 'text', text: i === 0 ? stub : '' }] }
|
|
77
|
+
: { ...w, text: i === 0 ? stub : '' });
|
|
64
78
|
const message = {
|
|
65
79
|
...data.message,
|
|
66
|
-
content: [...blocks.filter((b) => b
|
|
80
|
+
content: [...blocks.filter((b) => !carriers.includes(b)), ...stubbedCarriers],
|
|
67
81
|
};
|
|
68
82
|
try {
|
|
69
83
|
session.append('compaction/prune', {
|
package/lib/arrival.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrival.js","sourceRoot":"","sources":["../src/arrival.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAQ7C,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAA;AAe7C,MAAM,EAAE,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAA;AAErF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAA2B,EAAE,IAAiB;IACpF,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,KAAK,EAAE,IAAI,KAAK,aAAa;YAAE,SAAQ;QAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,IAEN,CAAA;QACb,IAAI,IAAI,EAAE,OAAO,KAAK,SAAS;YAAE,SAAQ;QACzC,oEAAoE;QACpE,8EAA8E;QAC9E,wEAAwE;QACxE,uEAAuE;QACvE,oBAAoB;QACpB,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"arrival.js","sourceRoot":"","sources":["../src/arrival.ts"],"names":[],"mappings":"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"}
|
package/lib/attach.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attaching a created child to the workspace — the loud version (#54).
|
|
3
|
+
*
|
|
4
|
+
* One step, five ways it can fail, and only one of them silent. AGENTS records that
|
|
5
|
+
* `workspaceRegistry.createCanonical(cwd)` → `attachSession(id)` is precisely what makes a
|
|
6
|
+
* programmatic session APPEAR in the UI ("an unattached session was the whole explanation
|
|
7
|
+
* for round 6's 'not listed'"), yet the door called it through two optional chains inside a
|
|
8
|
+
* `catch` that only warned the profile logger. On host 0.2.0-rc.2 the result is a child that
|
|
9
|
+
* is created, durable, present in `session_projcache` — and absent from the sidebar after a
|
|
10
|
+
* reload, while the door reports success. Silence in a required step is the bug; this module
|
|
11
|
+
* is the fix.
|
|
12
|
+
*
|
|
13
|
+
* The rule it encodes: **verify the user-visible fact, not the absence of a throw.** A
|
|
14
|
+
* successful `attachSession()` proves the method exists, not that the session is listed —
|
|
15
|
+
* so the check is "does this child appear in `sessionQuery.listSessions()`", polled briefly
|
|
16
|
+
* because the workspace catches up asynchronously.
|
|
17
|
+
*/
|
|
18
|
+
export interface AttachPorts {
|
|
19
|
+
/** the host's `workspaceRegistry` service, as far as it was resolved */
|
|
20
|
+
workspaceRegistry: unknown;
|
|
21
|
+
/** the host's `sessionQuery` service; absent ⇒ listing cannot be verified, said out loud */
|
|
22
|
+
sessionQuery: unknown;
|
|
23
|
+
/** the host's `sessionController` service; its `list()` is the surface the BROWSER reads.
|
|
24
|
+
* Absent ⇒ the gateway half of the verification is UNVERIFIED, said out loud. */
|
|
25
|
+
sessionController?: unknown;
|
|
26
|
+
/** the diagnostics seam (`src/diag.ts`): sink + logger, never throws */
|
|
27
|
+
diagnostic(where: string, message: string, error?: unknown): void;
|
|
28
|
+
childId: string;
|
|
29
|
+
cwd: string;
|
|
30
|
+
/** test seams; production uses the defaults */
|
|
31
|
+
pollIntervalMs?: number;
|
|
32
|
+
pollBudgetMs?: number;
|
|
33
|
+
}
|
|
34
|
+
export interface AttachOutcome {
|
|
35
|
+
/** the workspace actually took the session */
|
|
36
|
+
attached: boolean;
|
|
37
|
+
/** it appears in the session list. `undefined` = UNVERIFIABLE, which is NOT `true`. */
|
|
38
|
+
listed: boolean | undefined;
|
|
39
|
+
/** it appears in the GATEWAY list the browser renders and opens from. `undefined` = unverified.
|
|
40
|
+
* Written as `| undefined` deliberately: this repo compiles with
|
|
41
|
+
* `exactOptionalPropertyTypes`, where `?: boolean` forbids assigning undefined. */
|
|
42
|
+
gatewayListed?: boolean | undefined;
|
|
43
|
+
/** user-visible words for anything short of attached+listed — these reach the door result */
|
|
44
|
+
warnings: string[];
|
|
45
|
+
}
|
|
46
|
+
export declare function attachCreatedChild(ports: AttachPorts): Promise<AttachOutcome>;
|
package/lib/attach.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attaching a created child to the workspace — the loud version (#54).
|
|
3
|
+
*
|
|
4
|
+
* One step, five ways it can fail, and only one of them silent. AGENTS records that
|
|
5
|
+
* `workspaceRegistry.createCanonical(cwd)` → `attachSession(id)` is precisely what makes a
|
|
6
|
+
* programmatic session APPEAR in the UI ("an unattached session was the whole explanation
|
|
7
|
+
* for round 6's 'not listed'"), yet the door called it through two optional chains inside a
|
|
8
|
+
* `catch` that only warned the profile logger. On host 0.2.0-rc.2 the result is a child that
|
|
9
|
+
* is created, durable, present in `session_projcache` — and absent from the sidebar after a
|
|
10
|
+
* reload, while the door reports success. Silence in a required step is the bug; this module
|
|
11
|
+
* is the fix.
|
|
12
|
+
*
|
|
13
|
+
* The rule it encodes: **verify the user-visible fact, not the absence of a throw.** A
|
|
14
|
+
* successful `attachSession()` proves the method exists, not that the session is listed —
|
|
15
|
+
* so the check is "does this child appear in `sessionQuery.listSessions()`", polled briefly
|
|
16
|
+
* because the workspace catches up asynchronously.
|
|
17
|
+
*/
|
|
18
|
+
const shape = (v) => {
|
|
19
|
+
if (v === null)
|
|
20
|
+
return 'null';
|
|
21
|
+
if (v === undefined)
|
|
22
|
+
return 'undefined';
|
|
23
|
+
if (typeof v !== 'object')
|
|
24
|
+
return typeof v;
|
|
25
|
+
const keys = Object.keys(v).slice(0, 6);
|
|
26
|
+
return `${Array.isArray(v) ? 'array' : 'object'}{${keys.join(',')}}`;
|
|
27
|
+
};
|
|
28
|
+
export async function attachCreatedChild(ports) {
|
|
29
|
+
const warnings = [];
|
|
30
|
+
const registry = ports.workspaceRegistry;
|
|
31
|
+
if (typeof registry?.createCanonical !== 'function') {
|
|
32
|
+
const reason = `child ${ports.childId} could NOT be attached to the workspace: no "workspaceRegistry" service (or it exposes no createCanonical) — it may never appear in the sidebar`;
|
|
33
|
+
ports.diagnostic('attach:workspaceRegistry', reason, `resolved=${shape(ports.workspaceRegistry)}`);
|
|
34
|
+
return { attached: false, listed: undefined, warnings: [reason] };
|
|
35
|
+
}
|
|
36
|
+
let workspace;
|
|
37
|
+
try {
|
|
38
|
+
workspace = await registry.createCanonical(ports.cwd);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const reason = `child ${ports.childId} could NOT be attached: createCanonical("${ports.cwd}") failed: ${String(error?.message ?? error)}`;
|
|
42
|
+
ports.diagnostic('attach:createCanonical', reason, error);
|
|
43
|
+
return { attached: false, listed: undefined, warnings: [reason] };
|
|
44
|
+
}
|
|
45
|
+
if (typeof workspace?.attachSession !== 'function') {
|
|
46
|
+
// THE 0.2.x hypothesis: the host reshaped this return. Before this module the optional
|
|
47
|
+
// chain made it a no-op with no word said; now it is a named, diagnosable mismatch.
|
|
48
|
+
const reason = `child ${ports.childId} could NOT be attached: createCanonical returned no "attachSession" method (got ${shape(workspace)}) — the host workspace API changed shape; the child is durable but may not appear in the sidebar`;
|
|
49
|
+
ports.diagnostic('attach:attachSession', reason, `workspace=${shape(workspace)}`);
|
|
50
|
+
return { attached: false, listed: undefined, warnings: [reason] };
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
await workspace.attachSession(ports.childId);
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
const reason = `child ${ports.childId} attach was refused: ${String(error?.message ?? error)}`;
|
|
57
|
+
ports.diagnostic('attach:attachSession', reason, error);
|
|
58
|
+
return { attached: false, listed: undefined, warnings: [reason] };
|
|
59
|
+
}
|
|
60
|
+
// Attached. Now the part the throw can't tell us: is it actually LISTED?
|
|
61
|
+
const sq = ports.sessionQuery;
|
|
62
|
+
if (typeof sq?.listSessions !== 'function') {
|
|
63
|
+
return {
|
|
64
|
+
attached: true,
|
|
65
|
+
listed: undefined,
|
|
66
|
+
warnings: [`child ${ports.childId} attached, but its listing is UNVERIFIED — no sessionQuery service to ask`],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const intervalMs = ports.pollIntervalMs ?? 150;
|
|
70
|
+
const budgetMs = ports.pollBudgetMs ?? 1500;
|
|
71
|
+
const deadline = Date.now() + budgetMs;
|
|
72
|
+
for (;;) {
|
|
73
|
+
let ids = [];
|
|
74
|
+
try {
|
|
75
|
+
ids = (await sq.listSessions() ?? []).map((r) => r?.header?.id ?? '');
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
// a failed LOOKUP is not a failed session — say which one happened
|
|
79
|
+
const reason = `child ${ports.childId} attached, but its listing could not be verified: sessionQuery.listSessions failed: ${String(error?.message ?? error)}`;
|
|
80
|
+
ports.diagnostic('attach:verify', reason, error);
|
|
81
|
+
return { attached: true, listed: undefined, warnings: [reason] };
|
|
82
|
+
}
|
|
83
|
+
if (ids.includes(ports.childId)) {
|
|
84
|
+
// Listed by PERSISTENCE is not the same claim as listed to the BROWSER. The gateway's
|
|
85
|
+
// `sessionController.list()` is the surface the client renders and can `open()` against,
|
|
86
|
+
// and on 0.2.0-rc.2 a plugin-created child was present in one and absent from the other
|
|
87
|
+
// (measured: the door's roster note said `MISSING, 1 sessions` while `listSessions`
|
|
88
|
+
// listed it) — so a single boolean would have called that a success.
|
|
89
|
+
const sc = ports.sessionController;
|
|
90
|
+
if (typeof sc?.list !== 'function') {
|
|
91
|
+
// Silence here is what cost a round of the 0.2.x hunt: "no warning" got read as "the
|
|
92
|
+
// gateway lists it". An unverified surface is a STATE and must be delivered as one.
|
|
93
|
+
const reason = `child ${ports.childId} is listed in the durable session corpus, but its presence in the gateway (browser) session list could not be verified — no "sessionController" service to ask`;
|
|
94
|
+
ports.diagnostic('attach:gatewayList', reason, `resolved=${shape(ports.sessionController)}`);
|
|
95
|
+
return { attached: true, listed: true, gatewayListed: undefined, warnings: [reason] };
|
|
96
|
+
}
|
|
97
|
+
let gids = [];
|
|
98
|
+
try {
|
|
99
|
+
gids = (await sc.list() ?? []).map((r) => r?.sessionId ?? r?.header?.id ?? '');
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
const reason = `child ${ports.childId} is listed, but the gateway session list could not be verified: ${String(error?.message ?? error)}`;
|
|
103
|
+
ports.diagnostic('attach:gatewayList', reason, error);
|
|
104
|
+
return { attached: true, listed: true, gatewayListed: undefined, warnings: [reason] };
|
|
105
|
+
}
|
|
106
|
+
if (gids.includes(ports.childId))
|
|
107
|
+
return { attached: true, listed: true, gatewayListed: true, warnings: [] };
|
|
108
|
+
const reason = `child ${ports.childId} is in the durable session list but NOT in the gateway's session list (sessionController.list returned ${String(gids.length)} sessions) — the browser cannot show or open it, so the door will not be able to switch`;
|
|
109
|
+
ports.diagnostic('attach:gatewayList', reason, `gateway=${gids.slice(0, 4).join(',')}`);
|
|
110
|
+
return { attached: true, listed: true, gatewayListed: false, warnings: [reason] };
|
|
111
|
+
}
|
|
112
|
+
if (Date.now() + intervalMs > deadline) {
|
|
113
|
+
const reason = `child ${ports.childId} was attached but does not appear in the session list within ${String(budgetMs)}ms (${String(ids.length)} sessions listed) — the archive is durable; the sidebar may not show it`;
|
|
114
|
+
ports.diagnostic('attach:verify', reason, `listed=${ids.slice(0, 4).join(',')}`);
|
|
115
|
+
return { attached: true, listed: false, warnings: [reason] };
|
|
116
|
+
}
|
|
117
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=attach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../src/attach.ts"],"names":[],"mappings":"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"}
|
package/lib/auto-save.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface AutoSaveDeps {
|
|
|
31
31
|
save: (child: AutoSaveChildView) => Promise<{
|
|
32
32
|
archived: number;
|
|
33
33
|
skipped: string | null;
|
|
34
|
+
warnings: string[];
|
|
34
35
|
}>;
|
|
35
36
|
warn: (message: string) => void;
|
|
36
37
|
/** Observability seam: what each settlement decided. Outcome vocabulary pinned by tests. */
|
|
@@ -68,4 +69,77 @@ export interface AutoSaveWiring extends SaveSessionConfig {
|
|
|
68
69
|
* for the idle/afterPush ladders exactly like a `/chapters-save` that skipped
|
|
69
70
|
* the manual nudge. A test pins that nothing here calls runNow.
|
|
70
71
|
*/
|
|
72
|
+
/**
|
|
73
|
+
* The ONE payload-bearing durable read (docs/contract.md § Durable reads, issue #58).
|
|
74
|
+
*
|
|
75
|
+
* Door 5 needs it because a continuable child is disposed before `subagent/end`; the repair door
|
|
76
|
+
* (`/chapters-repair`, #60) needs the same thing because re-deriving a chapter is worthless if the read
|
|
77
|
+
* returns metadata rows. Two copies of "read the log" is how 12 children were archived as empty shells
|
|
78
|
+
* and the seam still looked alive: `listEvents` succeeds, returns 10 correctly-typed
|
|
79
|
+
* `SessionEventRecord`s, and none of them has `data`. So this function is exported and pinned by a
|
|
80
|
+
* source-shape row that fails if a door starts reading the log by hand again.
|
|
81
|
+
*/
|
|
82
|
+
export declare function readDurableSession(get: (name: string) => unknown, sessionId: string): Promise<AutoSaveChildView | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* The seventh archive moment (#61, Amendment A): **a rooted child that has never archived.**
|
|
85
|
+
*
|
|
86
|
+
* Door 5 is keyed on `origin: 'subagent'`. A `ch-*` continuation child is NOT a subagent — its header
|
|
87
|
+
* says `origin: user` with a `parentSession` — so no moment was watching it at all. The field case was
|
|
88
|
+
* `ch-dc3be3ab`: 2,363 events, 414 tool calls, 2 completed turns, `chapters: []`, `reservations: {}`.
|
|
89
|
+
* Not a failed archive: one never attempted. Its knowledge sits in the log and is invisible to every
|
|
90
|
+
* search on the machine, which is precisely what this plugin exists to prevent.
|
|
91
|
+
*
|
|
92
|
+
* This is the PURE gate — the listener below only does I/O, so the decision is testable (and reviewable)
|
|
93
|
+
* without a host.
|
|
94
|
+
*/
|
|
95
|
+
export declare function shouldForceChildArchive(input: {
|
|
96
|
+
header: {
|
|
97
|
+
origin?: string;
|
|
98
|
+
agentPreset?: string;
|
|
99
|
+
parentSession?: string;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Lineage, from ANY of its three real sources — measured 2026-10-22, because the first version of
|
|
103
|
+
* this gate asked the KERNEL HEADER and silently missed the field case: `ch-dc3be3ab` carries
|
|
104
|
+
* `parentSession: NONE, origin: undefined`. Lineage lives in (a) the registry row, which
|
|
105
|
+
* `chapters_continue` writes via `linkChild` at continue-core.ts:520, (b) the header, when the host
|
|
106
|
+
* sets one, and (c) the id itself — `ch-${newId()}` is minted by continue-core.ts:501, so the prefix
|
|
107
|
+
* is OUR OWN durable claim that this session is a continuation child. A row that never archived can
|
|
108
|
+
* still carry the link, and a session with no row at all still carries the prefix.
|
|
109
|
+
*/
|
|
110
|
+
lineageParent?: string | null | undefined;
|
|
111
|
+
chapterCount: number;
|
|
112
|
+
lastArchivedSeq: number;
|
|
113
|
+
anchor: number;
|
|
114
|
+
completedTurnsAboveWatermark: number;
|
|
115
|
+
minTurns: number;
|
|
116
|
+
enabled: boolean;
|
|
117
|
+
}): {
|
|
118
|
+
fire: boolean;
|
|
119
|
+
reason: string;
|
|
120
|
+
};
|
|
121
|
+
export interface ChildArchiveCtx {
|
|
122
|
+
logger?: {
|
|
123
|
+
warn?: (m: string) => void;
|
|
124
|
+
info?: (m: string) => void;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export interface ChildArchiveDeps {
|
|
128
|
+
ctx: ChildArchiveCtx;
|
|
129
|
+
/** A getter, not a value: the realm's store opens asynchronously and once (#61). */
|
|
130
|
+
store: () => Promise<RegistryStore>;
|
|
131
|
+
wiring: AutoSaveWiring;
|
|
132
|
+
/** config `childAutoArchive`, default true — the operator can close this door. */
|
|
133
|
+
enabled: boolean;
|
|
134
|
+
/** config `childAutoArchiveMinTurns`, default 4 — the lag that re-arms it after the first save. */
|
|
135
|
+
minTurns: number;
|
|
136
|
+
onArchived?: (id: string, records: number) => void;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The listener. Save-only — creates NO session and compacts NO surface, so the "no
|
|
140
|
+
* continuation-creating actions mid-step" rule is not engaged (the same reasoning that let door 5
|
|
141
|
+
* exist). Fires only on `turn/end`, reads the LIVE session's own payload-bearing events, and is
|
|
142
|
+
* watermark-idempotent: a re-run finds `nothing-new` and writes nothing.
|
|
143
|
+
*/
|
|
144
|
+
export declare function makeChildArchiveListener(deps: ChildArchiveDeps): (session: unknown, event: unknown) => void;
|
|
71
145
|
export declare function wireSubagentAutoSave(ctx: AutoSaveCtx, store: RegistryStore, wiring: AutoSaveWiring): (info: SubagentEndInfoLike) => void;
|