@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.
Files changed (119) hide show
  1. package/LICENSE +14 -0
  2. package/README.md +644 -0
  3. package/cordis.patch.yml +88 -0
  4. package/docs/architecture.md +460 -0
  5. package/docs/contract.md +375 -0
  6. package/docs/development.md +437 -0
  7. package/docs/host-compaction-seam.md +117 -0
  8. package/docs/knowledge-repo.md +730 -0
  9. package/docs/provider.md +98 -0
  10. package/docs/verify.md +200 -0
  11. package/lib/archive.d.ts +88 -0
  12. package/lib/archive.js +114 -0
  13. package/lib/archive.js.map +1 -0
  14. package/lib/arrival.d.ts +23 -0
  15. package/lib/arrival.js +86 -0
  16. package/lib/arrival.js.map +1 -0
  17. package/lib/artifact-query.d.ts +53 -0
  18. package/lib/artifact-query.js +130 -0
  19. package/lib/artifact-query.js.map +1 -0
  20. package/lib/client.js +234 -0
  21. package/lib/client.js.map +1 -0
  22. package/lib/commands.d.ts +28 -0
  23. package/lib/commands.js +305 -0
  24. package/lib/commands.js.map +1 -0
  25. package/lib/compose.d.ts +30 -0
  26. package/lib/compose.js +131 -0
  27. package/lib/compose.js.map +1 -0
  28. package/lib/continue-core.d.ts +153 -0
  29. package/lib/continue-core.js +252 -0
  30. package/lib/continue-core.js.map +1 -0
  31. package/lib/engine-core.d.ts +219 -0
  32. package/lib/engine-core.js +405 -0
  33. package/lib/engine-core.js.map +1 -0
  34. package/lib/engine.d.ts +211 -0
  35. package/lib/engine.js +571 -0
  36. package/lib/engine.js.map +1 -0
  37. package/lib/enrich-queue.d.ts +66 -0
  38. package/lib/enrich-queue.js +100 -0
  39. package/lib/enrich-queue.js.map +1 -0
  40. package/lib/enrich-store.d.ts +31 -0
  41. package/lib/enrich-store.js +103 -0
  42. package/lib/enrich-store.js.map +1 -0
  43. package/lib/enrich-wire.d.ts +73 -0
  44. package/lib/enrich-wire.js +209 -0
  45. package/lib/enrich-wire.js.map +1 -0
  46. package/lib/enrich.d.ts +73 -0
  47. package/lib/enrich.js +150 -0
  48. package/lib/enrich.js.map +1 -0
  49. package/lib/gitops.d.ts +107 -0
  50. package/lib/gitops.js +539 -0
  51. package/lib/gitops.js.map +1 -0
  52. package/lib/index.d.ts +81 -0
  53. package/lib/index.js +305 -0
  54. package/lib/index.js.map +1 -0
  55. package/lib/indexing.d.ts +85 -0
  56. package/lib/indexing.js +212 -0
  57. package/lib/indexing.js.map +1 -0
  58. package/lib/injections.d.ts +66 -0
  59. package/lib/injections.js +83 -0
  60. package/lib/injections.js.map +1 -0
  61. package/lib/notice.d.ts +60 -0
  62. package/lib/notice.js +84 -0
  63. package/lib/notice.js.map +1 -0
  64. package/lib/provider.d.ts +44 -0
  65. package/lib/provider.js +66 -0
  66. package/lib/provider.js.map +1 -0
  67. package/lib/redact.d.ts +24 -0
  68. package/lib/redact.js +85 -0
  69. package/lib/redact.js.map +1 -0
  70. package/lib/registry.d.ts +120 -0
  71. package/lib/registry.js +130 -0
  72. package/lib/registry.js.map +1 -0
  73. package/lib/render.d.ts +40 -0
  74. package/lib/render.js +280 -0
  75. package/lib/render.js.map +1 -0
  76. package/lib/repo.d.ts +78 -0
  77. package/lib/repo.js +184 -0
  78. package/lib/repo.js.map +1 -0
  79. package/lib/rules-commands.d.ts +24 -0
  80. package/lib/rules-commands.js +138 -0
  81. package/lib/rules-commands.js.map +1 -0
  82. package/lib/rules.d.ts +90 -0
  83. package/lib/rules.js +185 -0
  84. package/lib/rules.js.map +1 -0
  85. package/lib/search.d.ts +48 -0
  86. package/lib/search.js +152 -0
  87. package/lib/search.js.map +1 -0
  88. package/lib/signature.d.ts +28 -0
  89. package/lib/signature.js +149 -0
  90. package/lib/signature.js.map +1 -0
  91. package/lib/store.d.ts +130 -0
  92. package/lib/store.js +235 -0
  93. package/lib/store.js.map +1 -0
  94. package/lib/sync.d.ts +183 -0
  95. package/lib/sync.js +719 -0
  96. package/lib/sync.js.map +1 -0
  97. package/lib/tools.d.ts +84 -0
  98. package/lib/tools.js +581 -0
  99. package/lib/tools.js.map +1 -0
  100. package/lib/treedx/client.d.ts +57 -0
  101. package/lib/treedx/client.js +82 -0
  102. package/lib/treedx/client.js.map +1 -0
  103. package/lib/treedx/provider.d.ts +39 -0
  104. package/lib/treedx/provider.js +479 -0
  105. package/lib/treedx/provider.js.map +1 -0
  106. package/lib/treedx/state.d.ts +27 -0
  107. package/lib/treedx/state.js +65 -0
  108. package/lib/treedx/state.js.map +1 -0
  109. package/lib/types.d.ts +100 -0
  110. package/lib/types.js +19 -0
  111. package/lib/types.js.map +1 -0
  112. package/lib/vocabulary.d.ts +41 -0
  113. package/lib/vocabulary.js +118 -0
  114. package/lib/vocabulary.js.map +1 -0
  115. package/package.json +132 -0
  116. package/presets/chapters/agent.cordis.yml +272 -0
  117. package/presets/chapters/preset.yml +3 -0
  118. package/presets/probe-roots/agent.cordis.yml +11 -0
  119. package/presets/probe-roots/preset.yml +3 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * P2 S2 — the enrichment queue: one queue, pluggable triggers, hard idle
3
+ * guarantee (record §6.2, plan decisions).
4
+ *
5
+ * Isomorphic factory (host plane + realm engine each own an instance, like
6
+ * createSyncScheduler). Providers are the ONLY variance:
7
+ * afterPush — schedule(reason) called after each completed sync pass
8
+ * idle — a resettable debounce timer (enrichment.idleMs)
9
+ * both — the default
10
+ * manual — no auto triggers; the /chapters-enrich run command drains
11
+ *
12
+ * Invariants this module owns:
13
+ * - NEVER in a hot path: enrichOne is only reached from drain(), and
14
+ * drain() is only fired by trigger events that already completed the
15
+ * user's work (post-push) or by silence (idle) or a command.
16
+ * - Idempotency key = chapter number + rootSession + model. Re-enqueue
17
+ * after vocab change is NOT needed (vocab affects canonicalization at
18
+ * index build, not the model call); after a MODEL change it IS (key
19
+ * includes model, so the key naturally changes).
20
+ * - enabled:false ⇒ every entry point is a pure no-op (kill-switch exit
21
+ * criterion: the corpus stays fully functional, signatures-only).
22
+ * - Cross-plane double-run: the caller-supplied lock (store-dir, same
23
+ * pattern as sync's acquireLock) makes the realm + host queues safe on
24
+ * one workspace.
25
+ */
26
+ export interface EnrichQueueDeps {
27
+ /** enumerate chapters lacking current-model enrichment (cheap, durable-read only) */
28
+ listPending: (model: string, cap: number) => Promise<{
29
+ key: string;
30
+ label: string;
31
+ }[]>;
32
+ /** run the ladder for one chapter; must never throw (S1 core + persistence) */
33
+ enrichOne: (key: string) => Promise<{
34
+ ok: boolean;
35
+ skipped?: string;
36
+ }>;
37
+ /** current enrichment model id ('' = resolver said: conversation model — queue still needs a concrete string for keys) */
38
+ resolveModel: () => Promise<string>;
39
+ enabled: boolean;
40
+ trigger: 'afterPush' | 'idle' | 'both' | 'manual';
41
+ idleMs: number;
42
+ batchCap: number;
43
+ log: (msg: string) => void;
44
+ /** injectable timer for tests */
45
+ setTimer?: (fn: () => void, ms: number) => {
46
+ cancel: () => void;
47
+ };
48
+ }
49
+ export interface EnrichQueue {
50
+ /** a sync pass just completed (or any natural idle point) */
51
+ onSyncDone: (reason: string) => void;
52
+ /** archive activity happened — resets the idle timer */
53
+ noteActivity: () => void;
54
+ /** manual drain (command surface); returns processed count */
55
+ drainNow: () => Promise<number>;
56
+ /** queue state for /chapters-status + reports */
57
+ state: () => {
58
+ enabled: boolean;
59
+ trigger: string;
60
+ pendingEstimate: number;
61
+ lastBatch: number;
62
+ model: string | null;
63
+ };
64
+ dispose: () => void;
65
+ }
66
+ export declare function createEnrichQueue(deps: EnrichQueueDeps): EnrichQueue;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * P2 S2 — the enrichment queue: one queue, pluggable triggers, hard idle
3
+ * guarantee (record §6.2, plan decisions).
4
+ *
5
+ * Isomorphic factory (host plane + realm engine each own an instance, like
6
+ * createSyncScheduler). Providers are the ONLY variance:
7
+ * afterPush — schedule(reason) called after each completed sync pass
8
+ * idle — a resettable debounce timer (enrichment.idleMs)
9
+ * both — the default
10
+ * manual — no auto triggers; the /chapters-enrich run command drains
11
+ *
12
+ * Invariants this module owns:
13
+ * - NEVER in a hot path: enrichOne is only reached from drain(), and
14
+ * drain() is only fired by trigger events that already completed the
15
+ * user's work (post-push) or by silence (idle) or a command.
16
+ * - Idempotency key = chapter number + rootSession + model. Re-enqueue
17
+ * after vocab change is NOT needed (vocab affects canonicalization at
18
+ * index build, not the model call); after a MODEL change it IS (key
19
+ * includes model, so the key naturally changes).
20
+ * - enabled:false ⇒ every entry point is a pure no-op (kill-switch exit
21
+ * criterion: the corpus stays fully functional, signatures-only).
22
+ * - Cross-plane double-run: the caller-supplied lock (store-dir, same
23
+ * pattern as sync's acquireLock) makes the realm + host queues safe on
24
+ * one workspace.
25
+ */
26
+ export function createEnrichQueue(deps) {
27
+ let idleTimer = null;
28
+ let draining = null;
29
+ let lastBatch = 0;
30
+ let modelCache = null;
31
+ let pendingEstimate = 0;
32
+ const setTimer = deps.setTimer ?? ((fn, ms) => {
33
+ const t = setTimeout(fn, ms);
34
+ return { cancel: () => clearTimeout(t) };
35
+ });
36
+ const drain = (forced = false) => {
37
+ // auto drains honor the trigger choice; a forced drain (the command /
38
+ // manual surface) only honors the kill switch
39
+ if (!deps.enabled || (!forced && deps.trigger === 'manual'))
40
+ return Promise.resolve(0);
41
+ if (draining !== null)
42
+ return draining; // one batch at a time; requests collapse
43
+ draining = (async () => {
44
+ try {
45
+ const model = await deps.resolveModel();
46
+ modelCache = model;
47
+ const pending = await deps.listPending(model, deps.batchCap);
48
+ pendingEstimate = pending.length;
49
+ let done = 0;
50
+ for (const item of pending) {
51
+ const r = await deps.enrichOne(item.key);
52
+ if (r.ok)
53
+ done++;
54
+ }
55
+ lastBatch = done;
56
+ if (done > 0)
57
+ deps.log(`enrichment batch: ${done}/${pending.length} chapter(s) via ${model}`);
58
+ return done;
59
+ }
60
+ catch (error) {
61
+ deps.log(`enrichment drain failed (kept silent for the user): ${String(error)}`);
62
+ return 0;
63
+ }
64
+ finally {
65
+ draining = null;
66
+ }
67
+ })();
68
+ return draining;
69
+ };
70
+ return {
71
+ onSyncDone: (reason) => {
72
+ if (!deps.enabled)
73
+ return;
74
+ if (deps.trigger === 'afterPush' || deps.trigger === 'both') {
75
+ deps.log(`enrichment scheduled after sync (${reason})`);
76
+ void drain();
77
+ }
78
+ },
79
+ noteActivity: () => {
80
+ if (!deps.enabled || (deps.trigger !== 'idle' && deps.trigger !== 'both'))
81
+ return;
82
+ idleTimer?.cancel();
83
+ idleTimer = setTimer(() => { void drain(); }, deps.idleMs);
84
+ },
85
+ drainNow: async () => {
86
+ if (!deps.enabled)
87
+ return 0;
88
+ return await drain(true);
89
+ },
90
+ state: () => ({
91
+ enabled: deps.enabled,
92
+ trigger: deps.trigger,
93
+ pendingEstimate,
94
+ lastBatch,
95
+ model: modelCache,
96
+ }),
97
+ dispose: () => { idleTimer?.cancel(); idleTimer = null; },
98
+ };
99
+ }
100
+ //# sourceMappingURL=enrich-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrich-queue.js","sourceRoot":"","sources":["../src/enrich-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export interface ChapterDoc {
2
+ /** raw frontmatter lines (between the --- fences), verbatim */
3
+ fmLines: string[];
4
+ body: string;
5
+ }
6
+ export declare function parseChapterFile(text: string): ChapterDoc;
7
+ export declare const bodyHash: (body: string) => string;
8
+ /**
9
+ * Apply minimal frontmatter updates, body-guaranteed. expectedFileSha256 is
10
+ * the registry record's hash — which archive.ts computes over the WHOLE FILE
11
+ * (sha256(chapter.markdown)), the same value the tamper-check compares on-disk
12
+ * bytes against (archive.ts verify). The verbatim-BODY guarantee is the
13
+ * declared frontmatter anchor: a body that no longer matches its declared
14
+ * hash is corruption and refuses any write. Convention note: this guard once
15
+ * compared the registry's whole-file hash against the BODY hash — a mismatch
16
+ * on every real chapter, so enrichment silently never wrote (found by the
17
+ * enrich e2e 2026-09-22; the error was invisible behind the host logger).
18
+ * Callers MUST re-anchor the registry record with the returned fileSha256
19
+ * after a changed=true write (the ladder does).
20
+ */
21
+ export declare function updateChapterFrontmatter(filePath: string, updates: Record<string, string>, expectedFileSha256?: string): {
22
+ changed: boolean;
23
+ body: string;
24
+ fileSha256: string;
25
+ };
26
+ /** YAML-ish serializer for the provenance chain (nested block, newest first) */
27
+ export declare function renderGeneratedBlock(chain: {
28
+ by: string;
29
+ model?: string;
30
+ at?: string;
31
+ }[]): string;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * P2 S3 — provenance persistence (record §2.4/§6.2): the ONLY sanctioned
3
+ * chapter-file mutation in the system.
4
+ *
5
+ * A chapter file = YAML frontmatter + verbatim body. Enrichment may rewrite
6
+ * frontmatter fields it owns (title/summary/topics/generated); the body is
7
+ * untouchable, so EVERY write re-verifies the body hash before committing
8
+ * and refuses, loudly, on any mismatch. Frontmatter edits are LINE-BASED
9
+ * minimal replacements (order and unknown lines preserved — the file's own
10
+ * renderer stays the authority on shape); the write is atomic (tmp+rename);
11
+ * provenance chains grow, never truncate.
12
+ */
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import { createHash } from 'node:crypto';
16
+ const FM_DELIM = '---';
17
+ export function parseChapterFile(text) {
18
+ if (!text.startsWith(`${FM_DELIM}\n`))
19
+ throw new Error('chapter file lacks frontmatter');
20
+ let endLine = -1;
21
+ const lines = text.split('\n');
22
+ for (let i = 1; i < lines.length; i++) {
23
+ if (lines[i] === FM_DELIM) {
24
+ endLine = i;
25
+ break;
26
+ }
27
+ }
28
+ if (endLine === -1)
29
+ throw new Error('unterminated frontmatter');
30
+ return { fmLines: lines.slice(1, endLine), body: lines.slice(endLine + 1).join('\n') };
31
+ }
32
+ export const bodyHash = (body) => createHash('sha256').update(body, 'utf8').digest('hex');
33
+ /** replace-or-append one top-level key; block values arrive as multi-line strings */
34
+ function setFmLine(lines, key, value) {
35
+ const start = lines.findIndex((l) => new RegExp(`^${key}:`).test(l));
36
+ const replacement = value.split('\n').map((l, i) => (i === 0 ? `${key}: ${l}` : l));
37
+ if (start === -1)
38
+ return [...lines, ...replacement];
39
+ let end = start + 1;
40
+ while (end < lines.length && /^\s/.test(lines[end]))
41
+ end++;
42
+ return [...lines.slice(0, start), ...replacement, ...lines.slice(end)];
43
+ }
44
+ /**
45
+ * Apply minimal frontmatter updates, body-guaranteed. expectedFileSha256 is
46
+ * the registry record's hash — which archive.ts computes over the WHOLE FILE
47
+ * (sha256(chapter.markdown)), the same value the tamper-check compares on-disk
48
+ * bytes against (archive.ts verify). The verbatim-BODY guarantee is the
49
+ * declared frontmatter anchor: a body that no longer matches its declared
50
+ * hash is corruption and refuses any write. Convention note: this guard once
51
+ * compared the registry's whole-file hash against the BODY hash — a mismatch
52
+ * on every real chapter, so enrichment silently never wrote (found by the
53
+ * enrich e2e 2026-09-22; the error was invisible behind the host logger).
54
+ * Callers MUST re-anchor the registry record with the returned fileSha256
55
+ * after a changed=true write (the ladder does).
56
+ */
57
+ export function updateChapterFrontmatter(filePath, updates, expectedFileSha256) {
58
+ const text = fs.readFileSync(filePath, 'utf8');
59
+ const doc = parseChapterFile(text);
60
+ const hash = bodyHash(doc.body);
61
+ const fileHash = bodyHash(text);
62
+ // self-consistency: a declared bodySha256 that no longer matches the body
63
+ // means on-disk corruption — refuse before anything else
64
+ // render.ts anchors the body hash under 'sha256:' — accept either name as
65
+ // the declared guard value
66
+ const declared = doc.fmLines.find((l) => /^(?:bodySha256|sha256):/.test(l))?.split(': ')[1]?.trim();
67
+ // Two legitimate declared forms exist: current (hash of the parsed body,
68
+ // trailing newline included) and legacy (hash of bodyText before the
69
+ // writer appended '\n' — every chapter archived before the 2026-09-23
70
+ // convention fix). Either matches; anything else is corruption. Accepting
71
+ // the legacy form keeps already-archived chapters enrichable.
72
+ if (declared !== undefined && declared !== hash && declared !== bodyHash(doc.body.replace(/\n$/, ''))) {
73
+ throw new Error(`refusing to touch ${filePath}: declared body hash ${declared.slice(0, 12)}… != computed ${hash.slice(0, 12)}… — body altered on disk`);
74
+ }
75
+ if (expectedFileSha256 !== undefined && expectedFileSha256 !== fileHash) {
76
+ throw new Error(`refusing to rewrite ${filePath}: file hash ${fileHash.slice(0, 12)}… != registry ${expectedFileSha256.slice(0, 12)}…`);
77
+ }
78
+ let fm = doc.fmLines;
79
+ let changed = false;
80
+ for (const [k, v] of Object.entries(updates)) {
81
+ const next = setFmLine(fm, k, v);
82
+ if (next.join('\n') !== fm.join('\n')) {
83
+ fm = next;
84
+ changed = true;
85
+ }
86
+ }
87
+ if (!changed)
88
+ return { changed: false, body: doc.body, fileSha256: fileHash };
89
+ // every sanctioned write carries a body anchor; render.ts names it sha256
90
+ if (!fm.some((l) => /^(?:bodySha256|sha256):/.test(l)))
91
+ fm = setFmLine(fm, 'sha256', hash);
92
+ const nextText = `${FM_DELIM}\n${fm.join('\n')}\n${FM_DELIM}\n${doc.body}`;
93
+ const tmp = path.join(path.dirname(filePath), `.${path.basename(filePath)}.tmp-${process.pid}`);
94
+ fs.writeFileSync(tmp, nextText);
95
+ fs.renameSync(tmp, filePath);
96
+ return { changed: true, body: doc.body, fileSha256: bodyHash(nextText) };
97
+ }
98
+ /** YAML-ish serializer for the provenance chain (nested block, newest first) */
99
+ export function renderGeneratedBlock(chain) {
100
+ const inner = chain.map((m) => m.by === 'model' ? `\n - by: model\n model: ${m.model}\n at: ${m.at ?? ''}` : '\n - by: deterministic');
101
+ return inner.join('');
102
+ }
103
+ //# sourceMappingURL=enrich-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrich-store.js","sourceRoot":"","sources":["../src/enrich-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { type EnrichQueue } from './enrich-queue.ts';
2
+ import { type GeneratedProvenance, type GeneratedMark } from './enrich.ts';
3
+ export interface EnrichConfig {
4
+ enabled: boolean;
5
+ /** 'provider/model' or '' = follow the conversation (realm) / stored route (host) */
6
+ model: string;
7
+ trigger: 'afterPush' | 'idle' | 'both' | 'manual';
8
+ idleMs: number;
9
+ batchCap: number;
10
+ }
11
+ export interface EnrichWiringDeps {
12
+ store: {
13
+ get(id: string): Promise<{
14
+ chapters: {
15
+ number: number;
16
+ path: string;
17
+ title: string;
18
+ summary: string;
19
+ topics: string[];
20
+ sha256: string;
21
+ generated?: Record<string, GeneratedMark[]>;
22
+ }[];
23
+ }>;
24
+ put(id: string, state: unknown): Promise<void>;
25
+ sessions(): IterableIterator<[string, unknown]>;
26
+ getSetting(key: string): string | undefined;
27
+ putSetting(key: string, value: string): Promise<void>;
28
+ };
29
+ cwd(): string | undefined;
30
+ config: EnrichConfig;
31
+ /** one completion; may reject — the ladder catches */
32
+ fetch(prompt: string, route: {
33
+ provider: string;
34
+ model: string;
35
+ } | null): Promise<string>;
36
+ /** route resolution when neither override nor config names one (realm: live agent; host: persisted) */
37
+ conversationRoute(): {
38
+ provider: string;
39
+ model: string;
40
+ } | null;
41
+ scheduler?: {
42
+ schedule(cwd: string, why: string): void;
43
+ };
44
+ log(m: string): void;
45
+ warn(m: string): void;
46
+ setTimer?: (fn: () => void, ms: number) => {
47
+ cancel(): void;
48
+ };
49
+ }
50
+ export interface EnrichWiring {
51
+ queue: EnrichQueue;
52
+ /** manual drain for the command surface (cwd arms the workspace for files) */
53
+ runNow(cwd?: string): Promise<{
54
+ processed: number;
55
+ remaining: number;
56
+ }>;
57
+ setModelOverride(v: string | null): Promise<void>;
58
+ modelOverride(): string | null;
59
+ statusLine(): string;
60
+ /** pending chapter count for reports (full scan, un-capped) */
61
+ pendingCount(): Promise<number>;
62
+ /** the resolved auxiliary route (enrichment AND plot share it, P2 decision) */
63
+ auxRoute(): {
64
+ provider: string;
65
+ model: string;
66
+ } | null;
67
+ /** planes remember the workspace seen at sync/command time */
68
+ rememberCwd(cwd: string): void;
69
+ dispose(): void;
70
+ }
71
+ /** YAML block for the generated: frontmatter field (newest-first chains) */
72
+ export declare function renderGeneratedBlock(gen: GeneratedProvenance): string;
73
+ export declare function createEnrichWiring(deps: EnrichWiringDeps): EnrichWiring;
@@ -0,0 +1,209 @@
1
+ /**
2
+ * P2 S6 — enrichment wiring: binds the pure ladder (enrich.ts) and the
3
+ * guarded persistence (enrich-store.ts) to the registry domain and the sync
4
+ * scheduler, per plane.
5
+ *
6
+ * Division of labor (deliberate):
7
+ * - REALM engine owns the AUTO triggers (afterPush/idle/both): it sees the
8
+ * conversation's routed model (requestHeader) and fires after its own
9
+ * pushes/finalizes; it persists the resolved provider/model route and a
10
+ * state heartbeat into the shared settings table.
11
+ * - HOST plane runs the command surface (manual drains, model override,
12
+ * report/status) and reads the same tables; its queue is trigger-manual
13
+ * so auto work never double-runs.
14
+ *
15
+ * The ladder per chapter: build slice -> fetch -> validate; invalid -> ONE
16
+ * retry with a nudge; still invalid -> keep deterministic values (never
17
+ * blocks, never blanks). Frontmatter write goes through the body-hash guard;
18
+ * the registry record mirrors the new fields; a modified file is republished
19
+ * via the scheduler (debounced).
20
+ */
21
+ import fs from 'node:fs';
22
+ import path from 'node:path';
23
+ import { createEnrichQueue } from "./enrich-queue.js";
24
+ import { buildEnrichInput, parseEnrichResult, applyEnrich } from "./enrich.js";
25
+ import { parseChapterFile, updateChapterFrontmatter } from "./enrich-store.js";
26
+ const ROUTE_KEY = 'enrichment.route';
27
+ const STATE_KEY = 'enrichment.state';
28
+ const stripFence = (chunk) => chunk.trim().replace(/^```[a-z]*\n?/, '').replace(/\n?```\s*$/, '').trim();
29
+ /** YAML block for the generated: frontmatter field (newest-first chains) */
30
+ export function renderGeneratedBlock(gen) {
31
+ const lines = [];
32
+ for (const key of ['title', 'summary', 'topics']) {
33
+ const chain = gen[key];
34
+ if (chain === undefined || chain.length === 0)
35
+ continue;
36
+ lines.push(` ${key}:`);
37
+ for (const m of chain) {
38
+ if (m.by === 'model')
39
+ lines.push(` - by: model`, ` model: ${m.model}`, ` at: ${m.at ?? ''}`);
40
+ else
41
+ lines.push(` - by: deterministic`);
42
+ }
43
+ }
44
+ return '\n' + lines.join('\n');
45
+ }
46
+ function chaptersOf(state) {
47
+ const c = state?.chapters;
48
+ return Array.isArray(c) ? c : [];
49
+ }
50
+ export function createEnrichWiring(deps) {
51
+ const resolveRoute = () => {
52
+ const override = deps.store.getSetting('enrichment.model') ?? '';
53
+ const spec = (override !== '' ? override : deps.config.model).trim();
54
+ if (spec !== '' && spec !== 'conversation') {
55
+ const i = spec.indexOf('/');
56
+ if (i > 0)
57
+ return { provider: spec.slice(0, i), model: spec.slice(i + 1) };
58
+ return { provider: deps.conversationRoute()?.provider ?? '', model: spec };
59
+ }
60
+ const persisted = deps.store.getSetting(ROUTE_KEY);
61
+ if (persisted !== undefined) {
62
+ const i = persisted.indexOf('/');
63
+ if (i > 0)
64
+ return { provider: persisted.slice(0, i), model: persisted.slice(i + 1) };
65
+ }
66
+ return deps.conversationRoute();
67
+ };
68
+ const modelKey = () => {
69
+ const r = resolveRoute();
70
+ return r !== null && r.model !== '' ? `${r.provider}/${r.model}` : 'conversation';
71
+ };
72
+ let rememberedCwd;
73
+ const ladder = async (key) => {
74
+ const sep = key.lastIndexOf('#');
75
+ const sid = key.slice(0, sep);
76
+ const num = Number(key.slice(sep + 1));
77
+ const cwd = deps.cwd() ?? rememberedCwd;
78
+ if (cwd === undefined)
79
+ return { ok: false, skipped: 'no workspace cwd yet (a sync or command arms it)' };
80
+ try {
81
+ const state = await deps.store.get(sid);
82
+ const rec = state.chapters.find((c) => c.number === num);
83
+ if (rec === undefined)
84
+ return { ok: false, skipped: 'record gone' };
85
+ const file = path.join(cwd, rec.path);
86
+ if (!fs.existsSync(file))
87
+ return { ok: false, skipped: 'chapter file absent' };
88
+ const doc = parseChapterFile(fs.readFileSync(file, 'utf8'));
89
+ const body = doc.body;
90
+ const userChunks = body.split(/\*\*User[^*\n]*:\*\*/g).slice(1);
91
+ const asstChunks = body.split(/\*\*Assistant[^*\n]*:\*\*/g).slice(1);
92
+ const input = buildEnrichInput({ title: rec.title }, {
93
+ requests: userChunks.slice(-3).map(stripFence),
94
+ ...(asstChunks.length > 0 ? { firstAssistant: stripFence(asstChunks[0]) } : {}),
95
+ ...(asstChunks.length > 1 ? { lastAssistant: stripFence(asstChunks[asstChunks.length - 1]) } : asstChunks.length === 1 && userChunks.length === 0 ? { lastAssistant: stripFence(asstChunks[0]) } : {}),
96
+ }, body.slice(0, 8000));
97
+ const route = resolveRoute();
98
+ const key2 = modelKey();
99
+ let result = parseEnrichResult(await deps.fetch(input, route));
100
+ if (result === null) {
101
+ // one retry with a nudge; garbage stays non-fatal (record §6.1)
102
+ result = parseEnrichResult(await deps.fetch(input + '\nRemember: reply with ONLY the JSON object, no other text.', route));
103
+ }
104
+ const current = { title: rec.title, summary: rec.summary, topics: rec.topics, ...(rec.generated !== undefined ? { generated: rec.generated } : {}) };
105
+ const applied = applyEnrich(current, result, key2, new Date().toISOString());
106
+ if (!applied.changed)
107
+ return { ok: false, skipped: result === null ? 'model output invalid; deterministic kept' : 'already enriched' };
108
+ const v = applied.values;
109
+ const written = updateChapterFrontmatter(file, {
110
+ title: JSON.stringify(v.title),
111
+ summary: JSON.stringify(v.summary),
112
+ topics: `[${v.topics.map((t) => JSON.stringify(t)).join(', ')}]`,
113
+ generated: renderGeneratedBlock(v.generated),
114
+ }, typeof rec.sha256 === 'string' ? rec.sha256 : undefined);
115
+ await deps.store.put(sid, {
116
+ ...state,
117
+ chapters: state.chapters.map((c) => c.number === num
118
+ // re-anchor the registry's whole-file hash after the sanctioned write
119
+ // so the cite-time tamper check stays truthful for every descendant
120
+ ? { ...c, title: v.title, summary: v.summary, topics: v.topics, generated: v.generated, ...(written.changed ? { sha256: written.fileSha256 } : {}) }
121
+ : c),
122
+ });
123
+ deps.scheduler?.schedule(cwd, 'archive:enrichment');
124
+ return { ok: true };
125
+ }
126
+ catch (error) {
127
+ deps.warn(`enrichment failed for ${key}: ${String(error?.message ?? error).slice(0, 120)}`);
128
+ return { ok: false, skipped: 'error' };
129
+ }
130
+ };
131
+ const queue = createEnrichQueue({
132
+ listPending: async (modelKey2, cap) => {
133
+ const out = [];
134
+ for (const [sid, st] of deps.store.sessions()) {
135
+ for (const c of chaptersOf(st)) {
136
+ const top = c.generated?.title?.[0];
137
+ if (top?.by === 'model' && top.model === modelKey2)
138
+ continue;
139
+ out.push({ key: `${sid}#${c.number}`, label: c.title.slice(0, 48) });
140
+ if (out.length >= cap)
141
+ return out;
142
+ }
143
+ }
144
+ return out;
145
+ },
146
+ enrichOne: ladder,
147
+ resolveModel: async () => modelKey(),
148
+ enabled: deps.config.enabled,
149
+ trigger: deps.config.trigger,
150
+ idleMs: deps.config.idleMs,
151
+ batchCap: deps.config.batchCap,
152
+ log: (m) => {
153
+ deps.log(m);
154
+ const r = resolveRoute();
155
+ if (r !== null)
156
+ void deps.store.putSetting(ROUTE_KEY, `${r.provider}/${r.model}`).catch(() => undefined);
157
+ void deps.store.putSetting(STATE_KEY, JSON.stringify({ at: new Date().toISOString(), line: m })).catch(() => undefined);
158
+ },
159
+ ...(deps.setTimer !== undefined ? { setTimer: deps.setTimer } : {}),
160
+ });
161
+ return {
162
+ queue,
163
+ auxRoute: () => resolveRoute(),
164
+ rememberCwd(c) { rememberedCwd = c; },
165
+ async pendingCount() {
166
+ let n = 0;
167
+ const key2 = modelKey();
168
+ for (const [, st] of deps.store.sessions()) {
169
+ for (const c of chaptersOf(st)) {
170
+ const top = c.generated?.title?.[0];
171
+ if (!(top?.by === 'model' && top.model === key2))
172
+ n += 1;
173
+ }
174
+ }
175
+ return n;
176
+ },
177
+ async runNow(cwd) {
178
+ if (cwd !== undefined)
179
+ rememberedCwd = cwd;
180
+ const processed = await queue.drainNow();
181
+ return { processed, remaining: await this.pendingCount() };
182
+ },
183
+ async setModelOverride(v) {
184
+ if (v === null)
185
+ await deps.store.putSetting('enrichment.model', '');
186
+ else
187
+ await deps.store.putSetting('enrichment.model', v);
188
+ },
189
+ modelOverride() {
190
+ const o = deps.store.getSetting('enrichment.model') ?? '';
191
+ return o === '' ? null : o;
192
+ },
193
+ statusLine() {
194
+ if (!deps.config.enabled)
195
+ return 'enrichment: disabled (signatures-only corpus, fully functional)';
196
+ const override = this.modelOverride();
197
+ const model = override ?? (deps.config.model !== '' ? deps.config.model : 'conversation model');
198
+ const heartbeat = deps.store.getSetting(STATE_KEY);
199
+ let last = '';
200
+ try {
201
+ last = ` · last: ${JSON.parse(heartbeat ?? '{}').line ?? 'idle'}`;
202
+ }
203
+ catch { /* cosmetic */ }
204
+ return `enrichment: ${deps.config.trigger}, model ${model}${last}`;
205
+ },
206
+ dispose() { queue.dispose(); },
207
+ };
208
+ }
209
+ //# sourceMappingURL=enrich-wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enrich-wire.js","sourceRoot":"","sources":["../src/enrich-wire.ts"],"names":[],"mappings":"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"}