@north-light/crouter 0.3.208 → 0.3.210

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 (138) hide show
  1. package/dist/api/client.d.ts +3 -1
  2. package/dist/api/client.js +4 -0
  3. package/dist/api/dto/broker-ops.d.ts +2 -12
  4. package/dist/api/dto/nodes.d.ts +16 -0
  5. package/dist/api/routes.d.ts +1 -0
  6. package/dist/api/routes.js +1 -0
  7. package/dist/builtin-memory/00-runtime-base.md +3 -2
  8. package/dist/builtin-memory/01-spine/00-has-manager.md +3 -2
  9. package/dist/builtin-memory/01-spine/01-no-manager.md +3 -2
  10. package/dist/builtin-memory/02-lifecycle/00-terminal.md +3 -2
  11. package/dist/builtin-memory/02-lifecycle/01-resident.md +3 -2
  12. package/dist/builtin-memory/04-base-worker.md +3 -2
  13. package/dist/builtin-memory/04-orchestration-kernel.md +3 -2
  14. package/dist/builtin-memory/05-kinds/advisor/00-base.md +3 -2
  15. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +3 -2
  16. package/dist/builtin-memory/05-kinds/design/00-base.md +3 -2
  17. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +3 -2
  18. package/dist/builtin-memory/05-kinds/developer/00-base.md +3 -2
  19. package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +3 -2
  20. package/dist/builtin-memory/05-kinds/explore/00-base.md +3 -2
  21. package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +3 -2
  22. package/dist/builtin-memory/05-kinds/general/00-base.md +3 -2
  23. package/dist/builtin-memory/05-kinds/general/01-orchestrator.md +3 -2
  24. package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -2
  25. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +3 -2
  26. package/dist/builtin-memory/05-kinds/plan/reviewers/00-base.md +3 -2
  27. package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +3 -2
  28. package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +3 -2
  29. package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +3 -2
  30. package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +3 -2
  31. package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +3 -2
  32. package/dist/builtin-memory/05-kinds/review/00-base.md +3 -2
  33. package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +3 -2
  34. package/dist/builtin-memory/05-kinds/review/companion/00-base.md +3 -2
  35. package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -2
  36. package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -2
  37. package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -2
  38. package/dist/builtin-memory/advisor/council.md +0 -2
  39. package/dist/builtin-memory/design.md +3 -2
  40. package/dist/builtin-memory/development.md +3 -2
  41. package/dist/builtin-memory/insights/capture.md +1 -3
  42. package/dist/builtin-memory/insights/init.md +1 -3
  43. package/dist/builtin-memory/insights/listen.md +3 -2
  44. package/dist/builtin-memory/internal/INDEX.md +5 -4
  45. package/dist/builtin-memory/internal/agent-shaping.md +5 -4
  46. package/dist/builtin-memory/internal/examples/INDEX.md +3 -2
  47. package/dist/builtin-memory/internal/examples/imessage-assistant.md +3 -2
  48. package/dist/builtin-memory/internal/marketplaces.md +3 -2
  49. package/dist/builtin-memory/internal/memory-loading.md +31 -20
  50. package/dist/builtin-memory/internal/nodes-and-canvas.md +3 -2
  51. package/dist/builtin-memory/internal/plugins.md +7 -6
  52. package/dist/builtin-memory/internal/storage-tiers.md +3 -2
  53. package/dist/builtin-memory/plan/roadmap.md +3 -2
  54. package/dist/builtin-memory/spec/guide.md +0 -2
  55. package/dist/builtin-memory/spec/requirements.md +0 -2
  56. package/dist/builtin-memory/spec/roadmap.md +3 -2
  57. package/dist/builtin-memory/testing.md +1 -3
  58. package/dist/builtin-memory/wedged-child-on-runaway-bash.md +3 -2
  59. package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +1 -1
  60. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +3 -3
  61. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +1 -4
  62. package/dist/clients/attach/viewer.js +366 -366
  63. package/dist/commands/memory/delete.js +3 -18
  64. package/dist/commands/memory/edit.js +23 -30
  65. package/dist/commands/memory/history.js +1 -1
  66. package/dist/commands/memory/lint.d.ts +7 -8
  67. package/dist/commands/memory/lint.js +178 -132
  68. package/dist/commands/memory/list.d.ts +0 -1
  69. package/dist/commands/memory/list.js +2 -12
  70. package/dist/commands/memory/move.d.ts +1 -0
  71. package/dist/commands/memory/move.js +195 -0
  72. package/dist/commands/memory/read.js +135 -141
  73. package/dist/commands/memory/shared.d.ts +18 -17
  74. package/dist/commands/memory/shared.js +93 -39
  75. package/dist/commands/memory/write.js +23 -33
  76. package/dist/commands/memory.js +5 -4
  77. package/dist/commands/pkg/browse/catalog.js +2 -4
  78. package/dist/commands/pkg/browse/doc-view.js +17 -11
  79. package/dist/commands/pkg/browse/model.d.ts +7 -9
  80. package/dist/commands/sys/migrate.d.ts +1 -0
  81. package/dist/commands/sys/migrate.js +106 -0
  82. package/dist/commands/sys/sync-deps.js +5 -10
  83. package/dist/commands/sys/sync-project-guidance.js +36 -16
  84. package/dist/commands/sys/sync-skills.js +8 -4
  85. package/dist/commands/sys.js +3 -2
  86. package/dist/core/__tests__/inline-memory-refs.test.js +8 -5
  87. package/dist/core/__tests__/memory-resolver-precedence.test.js +5 -4
  88. package/dist/core/__tests__/nested-store-discovery.test.js +1 -3
  89. package/dist/core/__tests__/on-read-crouter-home-fence.test.js +3 -3
  90. package/dist/core/__tests__/on-read-dedup-resume.test.js +12 -12
  91. package/dist/core/__tests__/on-read-nested-store.test.js +23 -20
  92. package/dist/core/canvas/db.d.ts +3 -1
  93. package/dist/core/canvas/db.js +12 -2
  94. package/dist/core/memory/history.d.ts +4 -1
  95. package/dist/core/memory/history.js +1 -0
  96. package/dist/core/memory/inline-ref-inventory.d.ts +5 -4
  97. package/dist/core/memory/inline-ref-inventory.js +23 -19
  98. package/dist/core/memory-resolver.d.ts +4 -4
  99. package/dist/core/memory-resolver.js +16 -43
  100. package/dist/core/runtime/bearings.d.ts +4 -3
  101. package/dist/core/runtime/bearings.js +4 -4
  102. package/dist/core/runtime/broker-extension-render.d.ts +3 -2
  103. package/dist/core/runtime/broker-extension-render.js +3 -3
  104. package/dist/core/runtime/memory.js +2 -3
  105. package/dist/core/substrate/index.d.ts +7 -4
  106. package/dist/core/substrate/index.js +6 -4
  107. package/dist/core/substrate/injected-store.d.ts +24 -12
  108. package/dist/core/substrate/injected-store.js +80 -33
  109. package/dist/core/substrate/listings.d.ts +21 -0
  110. package/dist/core/substrate/listings.js +88 -0
  111. package/dist/core/substrate/on-read-node.d.ts +5 -5
  112. package/dist/core/substrate/on-read-node.js +4 -5
  113. package/dist/core/substrate/on-read.d.ts +25 -4
  114. package/dist/core/substrate/on-read.js +81 -102
  115. package/dist/core/substrate/render-node.d.ts +5 -2
  116. package/dist/core/substrate/render-node.js +5 -3
  117. package/dist/core/substrate/render.d.ts +9 -8
  118. package/dist/core/substrate/render.js +104 -96
  119. package/dist/core/substrate/schema.d.ts +34 -18
  120. package/dist/core/substrate/schema.js +75 -32
  121. package/dist/core/substrate/surface-match.d.ts +32 -0
  122. package/dist/core/substrate/surface-match.js +179 -0
  123. package/dist/daemon/api/handlers/nodes.js +9 -0
  124. package/dist/migrations/001-surfaces-frontmatter.d.ts +2 -0
  125. package/dist/migrations/001-surfaces-frontmatter.js +276 -0
  126. package/dist/migrations/convergent.d.ts +31 -0
  127. package/dist/migrations/convergent.js +71 -0
  128. package/dist/migrations/registry.d.ts +2 -0
  129. package/dist/migrations/registry.js +19 -0
  130. package/dist/migrations/types.d.ts +40 -0
  131. package/dist/migrations/types.js +11 -0
  132. package/dist/pi-extensions/canvas-context-intro.d.ts +2 -1
  133. package/dist/pi-extensions/canvas-doc-substrate.d.ts +2 -1
  134. package/dist/pi-extensions/canvas-doc-substrate.js +57 -34
  135. package/package.json +1 -1
  136. package/runtime.lock.json +2 -2
  137. package/dist/core/substrate/ceiling.d.ts +0 -17
  138. package/dist/core/substrate/ceiling.js +0 -67
@@ -1,45 +1,41 @@
1
- // on-read.ts — the pure file-context delivery functions for the document
2
- // substrate. Both take a caller-supplied subject; the canvas-db wrappers that
3
- // resolve one from a node id live in on-read-node.ts, so a consumer that
4
- // already holds a subject imports this module WITHOUT pulling subject.js's
5
- // canvas-db reach (the same split as render.ts / render-node.ts).
1
+ // on-read.ts — the pure event-delivery renderers for the document substrate.
2
+ // Each takes a caller-supplied subject; the canvas-db wrappers that resolve
3
+ // one from a node id live in on-read-node.ts, so a consumer that already
4
+ // holds a subject imports this module WITHOUT pulling subject.js's canvas-db
5
+ // reach (the same split as render.ts / render-node.ts).
6
6
  //
7
- // A memory with file-read visibility participates only through an explicit
8
- // `applies-to` path glob (authoring lint requires one). Two events use that one
9
- // routing mechanism:
7
+ // A doc participates through its `surfaces` entries (surface-match.ts owns
8
+ // the per-entry matchers; this module folds them per doc and renders):
10
9
  //
11
- // • A successful `read` tool call evaluates the read file against every
12
- // visible substrate doc. Project stores encountered between that file and
13
- // the filesystem root join the resolved cwd/profile corpus, so a nested
14
- // workspace can contribute guidance exactly when a file beneath it is read.
15
- // On this event the reserved `applies-to: "."` target means "the read file
16
- // is under the doc's store's owning directory" — a nested store's front
17
- // door surfaces on the first read beneath it, delivery driven purely by
18
- // filesystem truth.
19
- // • Opening a workspace evaluates the reserved `applies-to: "."` target for
20
- // every project store mounted by the cwd and selected profile. This happens
21
- // while the first-message bearings are assembled, so workspace-wide context
22
- // arrives before the task rather than waiting for an arbitrary first file.
23
- // Both hooks share one transcript-scoped dedup set, so a doc delivered at
24
- // workspace open never re-delivers on a later matching read.
10
+ // • `read` — a successful read tool call evaluates the read file against
11
+ // every doc's `read` entries: path globs vs the absolute path and
12
+ // basename, `./`-anchored globs vs the path relative to the doc's store's
13
+ // owning repo dir (project stores only), `match-frontmatter` vs the read
14
+ // file's own YAML. Project stores encountered between the file and the
15
+ // filesystem root join the resolved cwd/profile corpus, so a nested
16
+ // workspace contributes guidance exactly when a file beneath it is read.
17
+ // • `workspace-open` — opening a workspace delivers docs carrying a
18
+ // `workspace-open` entry from every project store mounted by the cwd and
19
+ // selected profile, while the first-message bearings are assembled — so
20
+ // workspace-wide context arrives before the task rather than waiting for
21
+ // an arbitrary first file.
22
+ // • `command` — a `bash` tool call evaluates the executed command string
23
+ // against every doc's `command` entries (string globs, `*` crossing `/`).
25
24
  //
26
- // `read-when` remains an additional OR trigger over a read markdown file's own
27
- // frontmatter. It does not replace `applies-to`: every non-none file-read rung
28
- // still declares its path boundary explicitly.
29
- //
30
- // Every candidate renders at its own fileReadVisibility rung. Explicit routing
31
- // is the whole on-read model, so directory INDEX ceilings remain a boot-catalog
32
- // concern and do not cap a deliberately matched document.
25
+ // All channels share one transcript-scoped (realpath → rung rank) dedup set,
26
+ // so a doc delivered by one never re-delivers at the same or lower rung
27
+ // through another. Each candidate renders at its matched rung — the highest
28
+ // `at` over its matching entries for the event.
33
29
  import { homedir } from 'node:os';
34
- import { basename, dirname, isAbsolute, matchesGlob, parse, relative, sep } from 'node:path';
30
+ import { dirname, parse, relative, sep } from 'node:path';
35
31
  import { CRTR_DIR_NAME } from '../../types.js';
36
32
  import { pathExists, readText, walkFiles } from '../fs-utils.js';
37
33
  import { parseFrontmatterGeneric } from '../frontmatter.js';
38
- import { evalCondition } from '../predicate.js';
39
34
  import { listAllMemoryDocs, listProjectMemoryDocs } from '../memory-resolver.js';
40
35
  import { userScopeRoot } from '../scope.js';
41
- import { displayName } from './ceiling.js';
42
36
  import { gatePasses } from './gate.js';
37
+ import { commandDeliveryRung, memoryReadDeliveryRung, owningRootOf, readDeliveryRung, workspaceOpenRung } from './surface-match.js';
38
+ import { deliveredAtOrAbove, recordDelivery } from './injected-store.js';
43
39
  import { normalizeDocName, parseSubstrateDoc, parseSubstrateFrontmatter, previewLine, resolveDocName } from './schema.js';
44
40
  import { cachedSubstrateDocs } from './session-cache.js';
45
41
  import { realpathOrSelf } from '../fs-utils.js';
@@ -75,13 +71,6 @@ function disp(p) {
75
71
  function isJunkAncestor(dir) {
76
72
  return dir.split(sep).some((segment) => JUNK_DIRS.has(segment));
77
73
  }
78
- function owningRootOf(doc) {
79
- const parts = doc.path.split(sep);
80
- const idx = parts.lastIndexOf(CRTR_DIR_NAME);
81
- if (idx <= 0)
82
- return null;
83
- return parts.slice(0, idx).join(sep) || sep;
84
- }
85
74
  function loadProjectDoc(file, memoryDir) {
86
75
  try {
87
76
  const raw = relative(memoryDir, file)
@@ -168,31 +157,6 @@ function dedupeByPhysicalPath(docs) {
168
157
  }
169
158
  return out;
170
159
  }
171
- function globMatches(glob, absReadFile, owningRoot) {
172
- const targets = [absReadFile, basename(absReadFile)];
173
- if (owningRoot !== null)
174
- targets.push(relative(owningRoot, absReadFile));
175
- return targets.some((target) => {
176
- try {
177
- return matchesGlob(target, glob);
178
- }
179
- catch {
180
- return false;
181
- }
182
- });
183
- }
184
- /** True when the read file sits beneath the doc's store's owning directory —
185
- * the read-event meaning of the reserved `applies-to: "."` target. Callers
186
- * additionally guard on `doc.scope === 'project'`: a profile store's path
187
- * computes an owning root of `~`, and user/builtin/node docs likewise have no
188
- * project-owning dir, so `.` outside a project store must never match. */
189
- function underOwningRoot(doc, absReadFile) {
190
- const root = owningRootOf(doc);
191
- if (root === null)
192
- return false;
193
- const rel = relative(realpathOrSelf(root), absReadFile);
194
- return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel);
195
- }
196
160
  function readFileFrontmatter(absReadFile) {
197
161
  if (!/\.(md|mdx|markdown)$/i.test(absReadFile))
198
162
  return {};
@@ -203,24 +167,7 @@ function readFileFrontmatter(absReadFile) {
203
167
  return {};
204
168
  }
205
169
  }
206
- function matchesReadEvent(doc, absReadFile, readFrontmatter) {
207
- const pathMatch = doc.appliesTo?.some((glob) => glob.trim() === '.'
208
- ? doc.scope === 'project' && underOwningRoot(doc, absReadFile)
209
- : globMatches(glob, absReadFile, owningRootOf(doc))) === true;
210
- const frontmatterMatch = doc.readWhen !== undefined &&
211
- Object.keys(readFrontmatter).length > 0 &&
212
- evalCondition(doc.readWhen, readFrontmatter);
213
- return pathMatch || frontmatterMatch;
214
- }
215
- function envelopeName(doc) {
216
- const displayed = displayName(doc.name);
217
- if (displayed !== '')
218
- return displayed;
219
- const root = owningRootOf(doc);
220
- return root === null ? doc.name : basename(root);
221
- }
222
- function renderDocEnvelope(doc) {
223
- const rung = doc.fileReadVisibility;
170
+ function renderDocEnvelope(doc, rung) {
224
171
  if (rung === 'none')
225
172
  return null;
226
173
  let body = '';
@@ -228,47 +175,56 @@ function renderDocEnvelope(doc) {
228
175
  body = doc.body.trim();
229
176
  else if (rung === 'preview')
230
177
  body = previewLine(doc);
231
- const attrs = `kind="${attr(doc.kind)}" name="${attr(envelopeName(doc))}" src="${attr(disp(doc.path))}"`;
178
+ const attrs = `kind="${attr(doc.kind)}" name="${attr(doc.name)}" src="${attr(disp(doc.path))}"`;
232
179
  return body === '' ? `<memory ${attrs} />` : `<memory ${attrs}>\n${body}\n</memory>`;
233
180
  }
234
- function renderCandidates(subject, docs, seen) {
181
+ /** Render candidates at their matched rungs, gate-checked and deduped against
182
+ * the transcript set (higher rungs pierce lower; a delivery records at its
183
+ * rendered rung). A `null` subject means no node identity is available for
184
+ * gate evaluation: gated docs are skipped, ungated docs still deliver. */
185
+ export function renderCandidateBlocks(subject, candidates, seen) {
235
186
  const rendered = [];
236
- for (const doc of docs) {
187
+ for (const { doc, rung } of candidates) {
237
188
  const real = realpathOrSelf(doc.path);
238
- if (seen.has(real))
189
+ if (deliveredAtOrAbove(seen, real, rung))
239
190
  continue;
240
191
  try {
241
- if (!gatePasses(doc, subject))
192
+ if (subject === null ? doc.gate !== undefined : !gatePasses(doc, subject))
242
193
  continue;
243
- const block = renderDocEnvelope(doc);
194
+ const block = renderDocEnvelope(doc, rung);
244
195
  if (block === null)
245
196
  continue;
246
- seen.add(real);
197
+ recordDelivery(seen, real, rung);
247
198
  rendered.push(block);
248
199
  }
249
200
  catch {
250
201
  continue;
251
202
  }
252
203
  }
204
+ return rendered;
205
+ }
206
+ function renderCandidates(subject, candidates, seen) {
207
+ const rendered = renderCandidateBlocks(subject, candidates, seen);
253
208
  return rendered.length === 0
254
209
  ? ''
255
210
  : `<auto-loaded-context>\n${rendered.join('\n')}\n</auto-loaded-context>`;
256
211
  }
257
- /** Surface docs matched by a successful read tool call using the caller-supplied
258
- * daemon snapshot subject. */
259
- export function renderOnReadDocsForSubject(subject, readFilePath, seen = new Set()) {
212
+ /** Surface docs whose `read` entries match a successfully read file, using
213
+ * the caller-supplied daemon snapshot subject. */
214
+ export function renderOnReadDocsForSubject(subject, readFilePath, seen = new Map()) {
260
215
  const absReadFile = realpathOrSelf(readFilePath);
261
216
  const readFrontmatter = readFileFrontmatter(absReadFile);
262
- const docs = dedupeByPhysicalPath([...enclosingProjectDocs(absReadFile), ...resolvedDocs()])
217
+ const candidates = dedupeByPhysicalPath([...enclosingProjectDocs(absReadFile), ...resolvedDocs()])
263
218
  .filter((doc) => realpathOrSelf(doc.path) !== absReadFile)
264
- .filter((doc) => matchesReadEvent(doc, absReadFile, readFrontmatter));
265
- return renderCandidates(subject, docs, seen);
219
+ .map((doc) => ({ doc, rung: readDeliveryRung(doc, absReadFile, readFrontmatter) }))
220
+ .filter(({ rung }) => rung !== 'none');
221
+ return renderCandidates(subject, candidates, seen);
266
222
  }
267
223
  /** Workspace boot docs using the snapshot's cwd/profile instead of a canvas
268
224
  * lookup. `seen` is the transcript-scoped dedup set shared with the on-read
269
225
  * hook, so a front door delivered here never re-delivers on a later read
270
226
  * beneath its store. */
271
- export function renderWorkspaceOpenDocsForSubject(subject, cwd, profileId, seen = new Set()) {
227
+ export function renderWorkspaceOpenDocsForSubject(subject, cwd, profileId, seen = new Map()) {
272
228
  let docs;
273
229
  try {
274
230
  docs = listProjectMemoryDocs(cwd, profileId)
@@ -278,13 +234,36 @@ export function renderWorkspaceOpenDocsForSubject(subject, cwd, profileId, seen
278
234
  catch {
279
235
  return '';
280
236
  }
281
- docs = dedupeByPhysicalPath(docs)
282
- .filter((doc) => doc.appliesTo?.some((glob) => glob.trim() === '.') === true)
237
+ const candidates = dedupeByPhysicalPath(docs)
238
+ .map((doc) => ({ doc, rung: workspaceOpenRung(doc) }))
239
+ .filter(({ rung }) => rung !== 'none')
283
240
  .sort((a, b) => {
284
- const aRoot = owningRootOf(a) ?? '';
285
- const bRoot = owningRootOf(b) ?? '';
241
+ const aRoot = owningRootOf(a.doc) ?? '';
242
+ const bRoot = owningRootOf(b.doc) ?? '';
286
243
  const depth = aRoot.split(sep).filter(Boolean).length - bRoot.split(sep).filter(Boolean).length;
287
- return depth || aRoot.localeCompare(bRoot) || a.path.localeCompare(b.path);
244
+ return depth || aRoot.localeCompare(bRoot) || a.doc.path.localeCompare(b.doc.path);
288
245
  });
289
- return renderCandidates(subject, docs, seen);
246
+ return renderCandidates(subject, candidates, seen);
247
+ }
248
+ /** Inner `<memory>` blocks for the memory-read event: docs whose
249
+ * `memory-read` entries match the doc a `crtr memory read` just resolved
250
+ * (known by `targetNames`; `excludeRealpath` keeps the resolved doc from
251
+ * delivering itself). Returns unwrapped blocks — the read leaf composes one
252
+ * `<auto-loaded-context>` envelope from these plus the directory listings. */
253
+ export function memoryReadDocBlocks(subject, excludeRealpath, targetNames, seen) {
254
+ const candidates = dedupeByPhysicalPath(resolvedDocs())
255
+ .filter((doc) => realpathOrSelf(doc.path) !== excludeRealpath)
256
+ .map((doc) => ({ doc, rung: memoryReadDeliveryRung(doc, targetNames) }))
257
+ .filter(({ rung }) => rung !== 'none');
258
+ return renderCandidateBlocks(subject, candidates, seen);
259
+ }
260
+ /** Surface docs whose `command` entries match an executed bash command
261
+ * string. The corpus is the resolved cwd/profile set only — a command has no
262
+ * file to walk enclosing stores from. `seen` is the same transcript-scoped
263
+ * dedup set as the other channels. */
264
+ export function renderOnCommandDocsForSubject(subject, command, seen = new Map()) {
265
+ const candidates = dedupeByPhysicalPath(resolvedDocs())
266
+ .map((doc) => ({ doc, rung: commandDeliveryRung(doc, command) }))
267
+ .filter(({ rung }) => rung !== 'none');
268
+ return renderCandidates(subject, candidates, seen);
290
269
  }
@@ -1,3 +1,4 @@
1
+ import type { InjectedDocs } from './injected-store.js';
1
2
  /** The system-prompt `<memory kind="preference">` block for `nodeId`: assemble
2
3
  * the node's subject from the canvas-db, render the pure block, then swap the
3
4
  * literal `$CRTR_CONTEXT_DIR` env-var token for the node's real absolute path
@@ -7,5 +8,7 @@
7
8
  export declare function renderPreferencesSection(nodeId: string): string;
8
9
  /** The first-message `<memory kind="knowledge">` block for `nodeId`: assemble
9
10
  * the node's subject from the canvas-db and render the consultable catalog.
10
- * Returns '' for an unknown id or when nothing is eligible. */
11
- export declare function renderKnowledgeBlock(nodeId: string): string;
11
+ * Returns '' for an unknown id or when nothing is eligible. `seen` (optional,
12
+ * write-only) receives each rendered doc at its displayed rung — boot
13
+ * delivery seeding for the transcript dedup map. */
14
+ export declare function renderKnowledgeBlock(nodeId: string, seen?: InjectedDocs): string;
@@ -20,10 +20,12 @@ export function renderPreferencesSection(nodeId) {
20
20
  }
21
21
  /** The first-message `<memory kind="knowledge">` block for `nodeId`: assemble
22
22
  * the node's subject from the canvas-db and render the consultable catalog.
23
- * Returns '' for an unknown id or when nothing is eligible. */
24
- export function renderKnowledgeBlock(nodeId) {
23
+ * Returns '' for an unknown id or when nothing is eligible. `seen` (optional,
24
+ * write-only) receives each rendered doc at its displayed rung — boot
25
+ * delivery seeding for the transcript dedup map. */
26
+ export function renderKnowledgeBlock(nodeId, seen) {
25
27
  const subject = assembleNodeSubject(nodeId);
26
28
  if (subject === null)
27
29
  return '';
28
- return renderKnowledgeForSubject(subject, nodeId);
30
+ return renderKnowledgeForSubject(subject, nodeId, seen);
29
31
  }
@@ -1,6 +1,7 @@
1
+ import { type InjectedDocs } from './injected-store.js';
1
2
  import { type SubstrateDoc } from './schema.js';
2
3
  import type { NodeConfigSubject } from './subject-fields.js';
3
- export declare function renderPreferencesForSubject(subject: NodeConfigSubject | null): {
4
+ export declare function renderPreferencesForSubject(subject: NodeConfigSubject | null, seen?: InjectedDocs): {
4
5
  block: string;
5
6
  contentDocs: SubstrateDoc[];
6
7
  tree: string;
@@ -12,10 +13,10 @@ export declare function renderPreferencesForSubject(subject: NodeConfigSubject |
12
13
  * first-wins dedup over a same-named node-local doc. GROUPED by rung exactly
13
14
  * like the preference block: content prose first (general→specific,
14
15
  * node-local last), then the preview/name catalog tree. Node-local docs are
15
- * floored HERE — a rung-none node-local doc's effective rung is bumped to
16
- * `name` before grouping, so it still surfaces as a bare-name tree entry
17
- * rather than collapsing into a `[+N more]` count (its body still never
18
- * renders: floored docs never reach the `content` rung). Procedural guidance
19
- * and factual references both live here as `knowledge`. Returns '' when
20
- * nothing is eligible. */
21
- export declare function renderKnowledgeForSubject(subject: NodeConfigSubject, nodeId: string): string;
16
+ * floored HERE — a bootless node-local doc's rung is bumped to `name` before
17
+ * grouping, so it still surfaces as a bare-name tree entry rather than
18
+ * collapsing into a `[+N more]` count (its body still never renders: floored
19
+ * docs never reach the `content` rung). Procedural guidance and factual
20
+ * references both live here as `knowledge`. Returns '' when nothing is
21
+ * eligible. */
22
+ export declare function renderKnowledgeForSubject(subject: NodeConfigSubject, nodeId: string, seen?: InjectedDocs): string;