@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.
- package/dist/api/client.d.ts +3 -1
- package/dist/api/client.js +4 -0
- package/dist/api/dto/broker-ops.d.ts +2 -12
- package/dist/api/dto/nodes.d.ts +16 -0
- package/dist/api/routes.d.ts +1 -0
- package/dist/api/routes.js +1 -0
- package/dist/builtin-memory/00-runtime-base.md +3 -2
- package/dist/builtin-memory/01-spine/00-has-manager.md +3 -2
- package/dist/builtin-memory/01-spine/01-no-manager.md +3 -2
- package/dist/builtin-memory/02-lifecycle/00-terminal.md +3 -2
- package/dist/builtin-memory/02-lifecycle/01-resident.md +3 -2
- package/dist/builtin-memory/04-base-worker.md +3 -2
- package/dist/builtin-memory/04-orchestration-kernel.md +3 -2
- package/dist/builtin-memory/05-kinds/advisor/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/design/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/developer/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/explore/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/general/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/general/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +3 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +3 -2
- package/dist/builtin-memory/05-kinds/review/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -2
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -2
- package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -2
- package/dist/builtin-memory/advisor/council.md +0 -2
- package/dist/builtin-memory/design.md +3 -2
- package/dist/builtin-memory/development.md +3 -2
- package/dist/builtin-memory/insights/capture.md +1 -3
- package/dist/builtin-memory/insights/init.md +1 -3
- package/dist/builtin-memory/insights/listen.md +3 -2
- package/dist/builtin-memory/internal/INDEX.md +5 -4
- package/dist/builtin-memory/internal/agent-shaping.md +5 -4
- package/dist/builtin-memory/internal/examples/INDEX.md +3 -2
- package/dist/builtin-memory/internal/examples/imessage-assistant.md +3 -2
- package/dist/builtin-memory/internal/marketplaces.md +3 -2
- package/dist/builtin-memory/internal/memory-loading.md +31 -20
- package/dist/builtin-memory/internal/nodes-and-canvas.md +3 -2
- package/dist/builtin-memory/internal/plugins.md +7 -6
- package/dist/builtin-memory/internal/storage-tiers.md +3 -2
- package/dist/builtin-memory/plan/roadmap.md +3 -2
- package/dist/builtin-memory/spec/guide.md +0 -2
- package/dist/builtin-memory/spec/requirements.md +0 -2
- package/dist/builtin-memory/spec/roadmap.md +3 -2
- package/dist/builtin-memory/testing.md +1 -3
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +3 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +3 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +1 -4
- package/dist/clients/attach/viewer.js +366 -366
- package/dist/commands/memory/delete.js +3 -18
- package/dist/commands/memory/edit.js +23 -30
- package/dist/commands/memory/history.js +1 -1
- package/dist/commands/memory/lint.d.ts +7 -8
- package/dist/commands/memory/lint.js +178 -132
- package/dist/commands/memory/list.d.ts +0 -1
- package/dist/commands/memory/list.js +2 -12
- package/dist/commands/memory/move.d.ts +1 -0
- package/dist/commands/memory/move.js +195 -0
- package/dist/commands/memory/read.js +135 -141
- package/dist/commands/memory/shared.d.ts +18 -17
- package/dist/commands/memory/shared.js +93 -39
- package/dist/commands/memory/write.js +23 -33
- package/dist/commands/memory.js +5 -4
- package/dist/commands/pkg/browse/catalog.js +2 -4
- package/dist/commands/pkg/browse/doc-view.js +17 -11
- package/dist/commands/pkg/browse/model.d.ts +7 -9
- package/dist/commands/sys/migrate.d.ts +1 -0
- package/dist/commands/sys/migrate.js +106 -0
- package/dist/commands/sys/sync-deps.js +5 -10
- package/dist/commands/sys/sync-project-guidance.js +36 -16
- package/dist/commands/sys/sync-skills.js +8 -4
- package/dist/commands/sys.js +3 -2
- package/dist/core/__tests__/inline-memory-refs.test.js +8 -5
- package/dist/core/__tests__/memory-resolver-precedence.test.js +5 -4
- package/dist/core/__tests__/nested-store-discovery.test.js +1 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +3 -3
- package/dist/core/__tests__/on-read-dedup-resume.test.js +12 -12
- package/dist/core/__tests__/on-read-nested-store.test.js +23 -20
- package/dist/core/canvas/db.d.ts +3 -1
- package/dist/core/canvas/db.js +12 -2
- package/dist/core/memory/history.d.ts +4 -1
- package/dist/core/memory/history.js +1 -0
- package/dist/core/memory/inline-ref-inventory.d.ts +5 -4
- package/dist/core/memory/inline-ref-inventory.js +23 -19
- package/dist/core/memory-resolver.d.ts +4 -4
- package/dist/core/memory-resolver.js +16 -43
- package/dist/core/runtime/bearings.d.ts +4 -3
- package/dist/core/runtime/bearings.js +4 -4
- package/dist/core/runtime/broker-extension-render.d.ts +3 -2
- package/dist/core/runtime/broker-extension-render.js +3 -3
- package/dist/core/runtime/memory.js +2 -3
- package/dist/core/substrate/index.d.ts +7 -4
- package/dist/core/substrate/index.js +6 -4
- package/dist/core/substrate/injected-store.d.ts +24 -12
- package/dist/core/substrate/injected-store.js +80 -33
- package/dist/core/substrate/listings.d.ts +21 -0
- package/dist/core/substrate/listings.js +88 -0
- package/dist/core/substrate/on-read-node.d.ts +5 -5
- package/dist/core/substrate/on-read-node.js +4 -5
- package/dist/core/substrate/on-read.d.ts +25 -4
- package/dist/core/substrate/on-read.js +81 -102
- package/dist/core/substrate/render-node.d.ts +5 -2
- package/dist/core/substrate/render-node.js +5 -3
- package/dist/core/substrate/render.d.ts +9 -8
- package/dist/core/substrate/render.js +104 -96
- package/dist/core/substrate/schema.d.ts +34 -18
- package/dist/core/substrate/schema.js +75 -32
- package/dist/core/substrate/surface-match.d.ts +32 -0
- package/dist/core/substrate/surface-match.js +179 -0
- package/dist/daemon/api/handlers/nodes.js +9 -0
- package/dist/migrations/001-surfaces-frontmatter.d.ts +2 -0
- package/dist/migrations/001-surfaces-frontmatter.js +276 -0
- package/dist/migrations/convergent.d.ts +31 -0
- package/dist/migrations/convergent.js +71 -0
- package/dist/migrations/registry.d.ts +2 -0
- package/dist/migrations/registry.js +19 -0
- package/dist/migrations/types.d.ts +40 -0
- package/dist/migrations/types.js +11 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +2 -1
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +2 -1
- package/dist/pi-extensions/canvas-doc-substrate.js +57 -34
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/core/substrate/ceiling.d.ts +0 -17
- package/dist/core/substrate/ceiling.js +0 -67
|
@@ -305,8 +305,7 @@ function matchNormalizedChild(dir, segment, want, ext = '.md') {
|
|
|
305
305
|
/** Resolve a NORMALIZED (numeric-prefix-stripped) segment path against a
|
|
306
306
|
* physical memory dir: walk intermediate segments matching each as a
|
|
307
307
|
* directory by normalized name, then resolve the final segment as either a
|
|
308
|
-
* `.md` file or a directory
|
|
309
|
-
* match even when a same-named file also exists). Either half is null when
|
|
308
|
+
* `.md` file or a directory. Either half is null when
|
|
310
309
|
* the segment path does not resolve that way. This is what makes
|
|
311
310
|
* `00-runtime-base.md` findable as `runtime-base` and `01-spine/00-has-manager`
|
|
312
311
|
* findable as `spine/has-manager` — the physical path keeps its pins, only
|
|
@@ -329,28 +328,16 @@ function resolveNormalizedPath(baseDir, segments, ext = '.md') {
|
|
|
329
328
|
* tree segment for segment with `.jsonl` in place of `.md`. Reuses the doc
|
|
330
329
|
* resolution rules so a log outlives its doc under the same name the doc had:
|
|
331
330
|
* numeric prefixes stay prefix-blind (`00-topic.md` logged at
|
|
332
|
-
* `.history/00-topic.jsonl` still answers to `topic`)
|
|
333
|
-
*
|
|
331
|
+
* `.history/00-topic.jsonl` still answers to `topic`). Returns null when
|
|
332
|
+
* nothing resolves. */
|
|
334
333
|
export function resolveHistoryLogPath(historyDir, segments) {
|
|
335
|
-
|
|
336
|
-
if (filePath)
|
|
337
|
-
return filePath;
|
|
338
|
-
if (dirPath) {
|
|
339
|
-
const index = matchNormalizedChild(dirPath, 'INDEX', 'file', '.jsonl');
|
|
340
|
-
if (index)
|
|
341
|
-
return index;
|
|
342
|
-
}
|
|
343
|
-
return null;
|
|
334
|
+
return resolveNormalizedPath(historyDir, segments, '.jsonl').filePath;
|
|
344
335
|
}
|
|
345
336
|
/** Direct full-path lookup of memory/<name>.md within ONE source. Returns that
|
|
346
337
|
* source's single hit, or undefined — a source can produce at most one direct
|
|
347
338
|
* match (native wins over plugin within the source, and at most one plugin's
|
|
348
|
-
* name can match).
|
|
349
|
-
*
|
|
350
|
-
* A directory INDEX is the cleaner contract: when `<name>.md` is absent but
|
|
351
|
-
* `<name>/INDEX.md` exists, the bare dir name (`taste`) resolves to the dir's
|
|
352
|
-
* INDEX doc — carrying the dir name as its identity. (`taste/INDEX` still
|
|
353
|
-
* resolves directly as the file path.)
|
|
339
|
+
* name can match). A directory name matches NO doc here: the read leaf
|
|
340
|
+
* answers a directory with its listing, never a stand-in document.
|
|
354
341
|
*
|
|
355
342
|
* `name`'s segments are already normalized (a caller-supplied identifier is
|
|
356
343
|
* never authored with a numeric prefix); resolution against the physical tree
|
|
@@ -362,40 +349,26 @@ function findMemoryMatchInSource(name, segments, source) {
|
|
|
362
349
|
// precedence), then its enabled plugins.
|
|
363
350
|
const dir = source.memoryDir;
|
|
364
351
|
if (dir) {
|
|
365
|
-
const { filePath
|
|
352
|
+
const { filePath } = resolveNormalizedPath(dir, segments);
|
|
366
353
|
if (!isLegacySkillDoc && filePath !== null)
|
|
367
354
|
return loadMemoryDoc(source.scope, dir, filePath, name);
|
|
368
|
-
if (dirPath !== null) {
|
|
369
|
-
const indexPath = join(dirPath, 'INDEX.md');
|
|
370
|
-
if (pathExists(indexPath))
|
|
371
|
-
return loadMemoryDoc(source.scope, dir, indexPath, name);
|
|
372
|
-
}
|
|
373
355
|
}
|
|
374
356
|
// Plugin memory dir: a `<plugin>/<rest>` name resolves against that enabled
|
|
375
357
|
// plugin's memory/ tree (the `<pluginName>/` mount that listAllMemoryDocs
|
|
376
|
-
// enumerates — `read` must resolve what `list` shows).
|
|
377
|
-
// (no slash) resolves the plugin-root INDEX.md, mirroring the native
|
|
378
|
-
// bare-dir-name -> INDEX.md contract for the plugin mount root.
|
|
358
|
+
// enumerates — `read` must resolve what `list` shows).
|
|
379
359
|
const slash = name.indexOf('/');
|
|
380
|
-
|
|
381
|
-
|
|
360
|
+
if (slash <= 0)
|
|
361
|
+
return undefined;
|
|
362
|
+
const pluginName = name.slice(0, slash);
|
|
363
|
+
const rest = name.slice(slash + 1);
|
|
382
364
|
for (const p of source.plugins) {
|
|
383
365
|
if (p.name !== pluginName)
|
|
384
366
|
continue;
|
|
385
367
|
const pdir = pluginMemoryDir(p);
|
|
386
|
-
const restSegments = rest
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
return loadMemoryDoc(source.scope, pdir, filePath, name);
|
|
391
|
-
}
|
|
392
|
-
// Bare name -> <plugin>/memory/INDEX.md; slashed name -> dir INDEX.
|
|
393
|
-
const pIndexDir = rest ? resolveNormalizedPath(pdir, restSegments).dirPath : pdir;
|
|
394
|
-
if (pIndexDir !== null) {
|
|
395
|
-
const pindex = join(pIndexDir, 'INDEX.md');
|
|
396
|
-
if (pathExists(pindex))
|
|
397
|
-
return loadMemoryDoc(source.scope, pdir, pindex, name);
|
|
398
|
-
}
|
|
368
|
+
const restSegments = rest.split('/');
|
|
369
|
+
const { filePath } = resolveNormalizedPath(pdir, restSegments);
|
|
370
|
+
if (restSegments.at(-1) !== 'SKILL' && filePath !== null)
|
|
371
|
+
return loadMemoryDoc(source.scope, pdir, filePath, name);
|
|
399
372
|
}
|
|
400
373
|
return undefined;
|
|
401
374
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Cron } from '../canvas/index.js';
|
|
2
|
+
import type { InjectedDocs } from '../substrate/injected-store.js';
|
|
2
3
|
/** The bearings custom type is shared with every attached renderer. */
|
|
3
4
|
export { CONTEXT_INTRO_CUSTOM_TYPE } from '../../shared/generated-context.js';
|
|
4
5
|
/** The `<project_context>` block — the `<environment>` snapshot only: a
|
|
@@ -71,10 +72,10 @@ export declare function buildWakeBearings(origin: WakeOrigin): string;
|
|
|
71
72
|
* from.
|
|
72
73
|
* 2. `<memory kind="knowledge">` — the consultable boot catalog, a SIBLING of
|
|
73
74
|
* the bearings (never nested under the context dir). Dropped when empty.
|
|
74
|
-
* 3. `<auto-loaded-context>` — memories whose `
|
|
75
|
-
* cwd/profile mounted their owning project store. Dropped when empty.
|
|
75
|
+
* 3. `<auto-loaded-context>` — memories whose `on: workspace-open` entry fires
|
|
76
|
+
* because cwd/profile mounted their owning project store. Dropped when empty.
|
|
76
77
|
* 4. `<project_context>` — the cwd/git environment snapshot. pi's own
|
|
77
78
|
* AGENTS.md/CLAUDE.md injection stays suppressed (`noContextFiles` in
|
|
78
79
|
* broker.ts); `crtr sys sync project-guidance` imports that guidance into
|
|
79
80
|
* the directory's `.crouter/memory/INDEX.md` front door. */
|
|
80
|
-
export declare function buildContextBearings(nodeId: string, seen?:
|
|
81
|
+
export declare function buildContextBearings(nodeId: string, seen?: InjectedDocs): string;
|
|
@@ -340,8 +340,8 @@ function managedWorktreeNote(node) {
|
|
|
340
340
|
* from.
|
|
341
341
|
* 2. `<memory kind="knowledge">` — the consultable boot catalog, a SIBLING of
|
|
342
342
|
* the bearings (never nested under the context dir). Dropped when empty.
|
|
343
|
-
* 3. `<auto-loaded-context>` — memories whose `
|
|
344
|
-
* cwd/profile mounted their owning project store. Dropped when empty.
|
|
343
|
+
* 3. `<auto-loaded-context>` — memories whose `on: workspace-open` entry fires
|
|
344
|
+
* because cwd/profile mounted their owning project store. Dropped when empty.
|
|
345
345
|
* 4. `<project_context>` — the cwd/git environment snapshot. pi's own
|
|
346
346
|
* AGENTS.md/CLAUDE.md injection stays suppressed (`noContextFiles` in
|
|
347
347
|
* broker.ts); `crtr sys sync project-guidance` imports that guidance into
|
|
@@ -365,8 +365,8 @@ export function buildContextBearings(nodeId, seen) {
|
|
|
365
365
|
bearings.push('', orchestratorContextNote(nodeId));
|
|
366
366
|
bearings.push('</crtr-bearings>');
|
|
367
367
|
// The consultable catalog rides BESIDE the bearings, not inside them.
|
|
368
|
-
const knowledge = renderKnowledgeBlock(nodeId);
|
|
369
|
-
// A workspace mount is the initial read of its root (`
|
|
368
|
+
const knowledge = renderKnowledgeBlock(nodeId, seen);
|
|
369
|
+
// A workspace mount is the initial read of its root (`on: workspace-open`).
|
|
370
370
|
const workspaceContext = renderWorkspaceOpenDocs(nodeId, seen);
|
|
371
371
|
// Hidden applet/situation-origin ambient context (see situational-context.ts)
|
|
372
372
|
// — the stored `situational` card, placed in the bearings BODY, so it is a
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { InjectedDocs } from '../substrate/injected-store.js';
|
|
1
2
|
import type { BrokerExtensionNodeDTO, BrokerExtensionStateDTO } from '../../api/dto/broker-ops.js';
|
|
2
3
|
export declare function editorLabelForBrokerNode(node: BrokerExtensionNodeDTO): string;
|
|
3
4
|
/** Same environment-only project context rendered at normal boot, without a
|
|
@@ -6,9 +7,9 @@ export declare function buildProjectContextBlockForBroker(cwd: string): string;
|
|
|
6
7
|
/** `situational` is the node's stored situational card, placed in the bearings
|
|
7
8
|
* BODY — the one place in the system where a runtime card nests inside
|
|
8
9
|
* another (see situational-context.ts). '' when none is set. */
|
|
9
|
-
export declare function buildContextBearingsFromState(state: BrokerExtensionStateDTO, projectContext: string, situational?: string, seen?:
|
|
10
|
+
export declare function buildContextBearingsFromState(state: BrokerExtensionStateDTO, projectContext: string, situational?: string, seen?: InjectedDocs): string;
|
|
10
11
|
export declare function buildForkBearingsFromState(state: BrokerExtensionStateDTO, situational?: string): string;
|
|
11
|
-
export declare function renderPreferencesFromState(state: BrokerExtensionStateDTO): string;
|
|
12
|
+
export declare function renderPreferencesFromState(state: BrokerExtensionStateDTO, seen?: InjectedDocs): string;
|
|
12
13
|
/** A canonical report ref has already been validated by crtrd in state. The
|
|
13
14
|
* broker reads only its local filesystem content; no canvas accessor is needed. */
|
|
14
15
|
export declare function reportPathFromRef(ref: string, reportNodes: ReadonlyMap<string, unknown>): string | null;
|
|
@@ -158,7 +158,7 @@ export function buildContextBearingsFromState(state, projectContext, situational
|
|
|
158
158
|
if (node.mode === 'orchestrator')
|
|
159
159
|
bearings.push('', `Because you persist across refresh cycles, your context directory (${contextDir(node.node_id)}) is also where a future cycle of you resumes the work — keep the working notes and decisions a refreshed you would need there, alongside the docs you share with the nodes you spawn.`);
|
|
160
160
|
bearings.push('</crtr-bearings>');
|
|
161
|
-
const knowledge = renderKnowledgeForSubject(state.subject, node.node_id);
|
|
161
|
+
const knowledge = renderKnowledgeForSubject(state.subject, node.node_id, seen);
|
|
162
162
|
const workspace = renderWorkspaceOpenDocsForSubject(state.subject, node.cwd, node.profile_id, seen);
|
|
163
163
|
return [bearings.join('\n'), knowledge, workspace, situational, projectContext].filter((part) => part !== '').join('\n');
|
|
164
164
|
}
|
|
@@ -186,8 +186,8 @@ export function buildForkBearingsFromState(state, situational = '') {
|
|
|
186
186
|
bearings.push('', 'The first real user message after these bearings is your mandate.', '</crtr-bearings>');
|
|
187
187
|
return [bearings.join('\n'), situational].filter((part) => part !== '').join('\n');
|
|
188
188
|
}
|
|
189
|
-
export function renderPreferencesFromState(state) {
|
|
190
|
-
return interpolateNodePaths(renderPreferencesForSubject(state.subject).block, state.node.node_id);
|
|
189
|
+
export function renderPreferencesFromState(state, seen) {
|
|
190
|
+
return interpolateNodePaths(renderPreferencesForSubject(state.subject, seen).block, state.node.node_id);
|
|
191
191
|
}
|
|
192
192
|
/** A canonical report ref has already been validated by crtrd in state. The
|
|
193
193
|
* broker reads only its local filesystem content; no canvas accessor is needed. */
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// memory.ts — the node-local substrate document store.
|
|
2
2
|
//
|
|
3
3
|
// One scope lives here: node-local memory, a `memory/` directory of substrate
|
|
4
|
-
// documents (.md files with typed frontmatter — kind, when
|
|
5
|
-
//
|
|
6
|
-
// node. User-global and project memory are scope-resolved stores — see
|
|
4
|
+
// documents (.md files with typed frontmatter — kind, when-and-why-to-read,
|
|
5
|
+
// surfaces, gate) inside a node's context dir; it dies with the node. User-global and project memory are scope-resolved stores — see
|
|
7
6
|
// `scopeMemoryDir` in core/scope.ts.
|
|
8
7
|
import { join } from 'node:path';
|
|
9
8
|
// Import the pure path helper from paths.ts directly, NOT the canvas/index.js
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
export { KINDS, isDocKind, RUNGS, rungRank, rungAtLeast,
|
|
2
|
-
export type { DocKind, Rung, GatePredicate, SubstrateSchema, SubstrateDoc } from './schema.js';
|
|
1
|
+
export { KINDS, isDocKind, RUNGS, rungRank, rungAtLeast, SURFACE_EVENTS, SURFACE_RUNGS, bootRung, parseSubstrateFrontmatter, parseSubstrateDoc, previewLine, normalizeNameSegment, normalizeDocName, resolveDocName, } from './schema.js';
|
|
2
|
+
export type { DocKind, Rung, GatePredicate, SubstrateSchema, SubstrateDoc, SurfaceEntry, SurfaceEvent, SurfaceRung } from './schema.js';
|
|
3
|
+
export { matchesReadEntry, matchesMemoryReadEntry, matchesCommandEntry, readDeliveryRung, workspaceOpenRung, memoryReadDeliveryRung, commandDeliveryRung, owningRootOf, underOwningRoot } from './surface-match.js';
|
|
4
|
+
export { dirDedupKey, docsByName, isDirName, ancestorDirsOf, renderDirListing } from './listings.js';
|
|
5
|
+
export { deliveredAtOrAbove, recordDelivery, mergeInjectedDocs } from './injected-store.js';
|
|
6
|
+
export type { InjectedDocs } from './injected-store.js';
|
|
3
7
|
export { scopeForCwd, profileNameFor } from './subject-fields.js';
|
|
4
8
|
export type { NodeConfigSubject } from './subject-fields.js';
|
|
5
9
|
export { spineDepth, assembleNodeSubject } from './subject.js';
|
|
6
10
|
export { gatePasses } from './gate.js';
|
|
7
|
-
export { INDEX_NAME, isIndexName, indexDirOf, displayName, buildCeilingIndex, effectiveSystemPromptRung, } from './ceiling.js';
|
|
8
11
|
export { renderPreferencesForSubject, renderKnowledgeForSubject } from './render.js';
|
|
9
12
|
export { renderPreferencesSection, renderKnowledgeBlock } from './render-node.js';
|
|
10
|
-
export { renderOnReadDocsForSubject, renderWorkspaceOpenDocsForSubject } from './on-read.js';
|
|
13
|
+
export { renderOnReadDocsForSubject, renderWorkspaceOpenDocsForSubject, renderOnCommandDocsForSubject, renderCandidateBlocks, memoryReadDocBlocks } from './on-read.js';
|
|
11
14
|
export { renderOnReadDocs, renderWorkspaceOpenDocs } from './on-read-node.js';
|
|
@@ -8,19 +8,21 @@ export {
|
|
|
8
8
|
KINDS, isDocKind,
|
|
9
9
|
// ladder
|
|
10
10
|
RUNGS, rungRank, rungAtLeast,
|
|
11
|
-
//
|
|
12
|
-
|
|
11
|
+
// surfaces (event routing)
|
|
12
|
+
SURFACE_EVENTS, SURFACE_RUNGS, bootRung,
|
|
13
13
|
// parse + render-shared helpers
|
|
14
14
|
parseSubstrateFrontmatter, parseSubstrateDoc, previewLine,
|
|
15
15
|
// display-name normalization
|
|
16
16
|
normalizeNameSegment, normalizeDocName,
|
|
17
17
|
// substrate identity (explicit-name-then-path-fallback)
|
|
18
18
|
resolveDocName, } from './schema.js';
|
|
19
|
+
export { matchesReadEntry, matchesMemoryReadEntry, matchesCommandEntry, readDeliveryRung, workspaceOpenRung, memoryReadDeliveryRung, commandDeliveryRung, owningRootOf, underOwningRoot } from './surface-match.js';
|
|
20
|
+
export { dirDedupKey, docsByName, isDirName, ancestorDirsOf, renderDirListing } from './listings.js';
|
|
21
|
+
export { deliveredAtOrAbove, recordDelivery, mergeInjectedDocs } from './injected-store.js';
|
|
19
22
|
export { scopeForCwd, profileNameFor } from './subject-fields.js';
|
|
20
23
|
export { spineDepth, assembleNodeSubject } from './subject.js';
|
|
21
24
|
export { gatePasses } from './gate.js';
|
|
22
|
-
export { INDEX_NAME, isIndexName, indexDirOf, displayName, buildCeilingIndex, effectiveSystemPromptRung, } from './ceiling.js';
|
|
23
25
|
export { renderPreferencesForSubject, renderKnowledgeForSubject } from './render.js';
|
|
24
26
|
export { renderPreferencesSection, renderKnowledgeBlock } from './render-node.js';
|
|
25
|
-
export { renderOnReadDocsForSubject, renderWorkspaceOpenDocsForSubject } from './on-read.js';
|
|
27
|
+
export { renderOnReadDocsForSubject, renderWorkspaceOpenDocsForSubject, renderOnCommandDocsForSubject, renderCandidateBlocks, memoryReadDocBlocks } from './on-read.js';
|
|
26
28
|
export { renderOnReadDocs, renderWorkspaceOpenDocs } from './on-read-node.js';
|
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
1
|
+
import { type Rung } from './schema.js';
|
|
2
|
+
/** realpath → highest rung rank delivered this transcript (1=name, 2=preview,
|
|
3
|
+
* 3=content — `rungRank` of the delivered rung). */
|
|
4
|
+
export type InjectedDocs = Map<string, number>;
|
|
5
|
+
/** Already delivered at `rung` or above? — the skip test every channel runs
|
|
6
|
+
* before rendering a doc (or a listing line, at preview) again. */
|
|
7
|
+
export declare function deliveredAtOrAbove(seen: InjectedDocs, realpath: string, rung: Rung): boolean;
|
|
8
|
+
/** Record a delivery at `rung`, keeping the highest rank seen. */
|
|
9
|
+
export declare function recordDelivery(seen: InjectedDocs, realpath: string, rung: Rung): void;
|
|
10
|
+
/** Max-merge `source` into `target` — used to fold an out-of-process delivery
|
|
11
|
+
* (the daemon's warm-spare bearings push) into a live process's shared map. */
|
|
12
|
+
export declare function mergeInjectedDocs(target: InjectedDocs, source: InjectedDocs): void;
|
|
13
|
+
/** The process-shared dedup map for a node: loaded from disk once, then the
|
|
14
|
+
* SAME Map instance on every call, so all hooks in this pi process grow and
|
|
15
|
+
* consult one map. Daemon-side deliveries (a warm-spare claim) write the disk
|
|
4
16
|
* file from another process; consumers that must see those merge the plain
|
|
5
17
|
* `loadInjectedDocs` result in before rendering. */
|
|
6
|
-
export declare function sharedInjectedDocs(nodeId: string):
|
|
7
|
-
/** Rehydrate a node's
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
export declare function loadInjectedDocs(nodeId: string):
|
|
11
|
-
/** Persist a node's
|
|
12
|
-
*
|
|
13
|
-
export declare function saveInjectedDocs(nodeId: string, seen:
|
|
14
|
-
/** Drop a node's persisted dedup
|
|
18
|
+
export declare function sharedInjectedDocs(nodeId: string): InjectedDocs;
|
|
19
|
+
/** Rehydrate a node's dedup map from disk. Returns an empty map when the file
|
|
20
|
+
* is absent, unreadable, or malformed (a fresh transcript, or a node that has
|
|
21
|
+
* not yet surfaced any doc). */
|
|
22
|
+
export declare function loadInjectedDocs(nodeId: string): InjectedDocs;
|
|
23
|
+
/** Persist a node's dedup map. Called after each delivery that grew it, so
|
|
24
|
+
* the grown map survives a later dormancy. */
|
|
25
|
+
export declare function saveInjectedDocs(nodeId: string, seen: InjectedDocs): void;
|
|
26
|
+
/** Drop a node's persisted dedup map. Called by the launch paths that start a
|
|
15
27
|
* FRESH transcript, so the new conversation surfaces docs from scratch. */
|
|
16
28
|
export declare function clearInjectedDocs(nodeId: string): void;
|
|
@@ -1,32 +1,63 @@
|
|
|
1
|
-
// injected-store.ts — durable per-node dedup
|
|
1
|
+
// injected-store.ts — durable per-node dedup map for substrate doc delivery.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// so a given doc surfaces at most
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
3
|
+
// Delivery channels (boot render seeding, workspace-open, on-read, memory-read
|
|
4
|
+
// listings and events, command events) dedup so a given doc surfaces at most
|
|
5
|
+
// once per conversation AT A GIVEN DISCLOSURE. The map keys on realpath and
|
|
6
|
+
// carries the highest rung rank delivered so far; higher rungs pierce lower —
|
|
7
|
+
// a listing line (preview rank) must not block a later explicit content
|
|
8
|
+
// delivery, while a content delivery silences everything after it. Directory
|
|
9
|
+
// listings participate under the directory's own realpath (a directory never
|
|
10
|
+
// collides with a file's realpath).
|
|
11
11
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
12
|
+
// The map USED to live only in the pi process heap, cleared on session_start.
|
|
13
|
+
// But a node's logical session — the .jsonl transcript — spans MULTIPLE pi
|
|
14
|
+
// processes: a dormancy → revive(resume) cycle exits the old process and
|
|
15
|
+
// launches a fresh `pi --session` that REUSES the same transcript. This module
|
|
16
|
+
// persists the map to `nodes/<id>/injected-docs.json` so the resumed process
|
|
17
|
+
// rehydrates it and skips docs already present in the transcript. The one
|
|
18
|
+
// launch path that begins a FRESH transcript (reviveNode with resume=false, in
|
|
19
|
+
// runtime/revive.ts) calls clearInjectedDocs(), so a new conversation starts
|
|
20
|
+
// empty. CLI processes (the `memory read` leaf) load/save the same file.
|
|
17
21
|
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
22
|
+
// Disk format: `{"v":2,"docs":{"<realpath>":<rank 1|2|3>}}` (rank =
|
|
23
|
+
// rungRank of the delivered rung). A legacy v1 string-array rehydrates at
|
|
24
|
+
// content rank — its entries were recorded under never-re-deliver semantics.
|
|
25
|
+
//
|
|
26
|
+
// All ops are best-effort: a failed read/write degrades to a possible
|
|
27
|
+
// re-inject, never a crash — a dedup miss must never break a read or a revive.
|
|
20
28
|
import { readFileSync, writeFileSync, rmSync } from 'node:fs';
|
|
21
29
|
import { injectedDocsPath } from '../canvas/paths.js';
|
|
22
|
-
|
|
30
|
+
import { rungRank } from './schema.js';
|
|
31
|
+
/** Already delivered at `rung` or above? — the skip test every channel runs
|
|
32
|
+
* before rendering a doc (or a listing line, at preview) again. */
|
|
33
|
+
export function deliveredAtOrAbove(seen, realpath, rung) {
|
|
34
|
+
const have = seen.get(realpath);
|
|
35
|
+
return have !== undefined && have >= rungRank(rung);
|
|
36
|
+
}
|
|
37
|
+
/** Record a delivery at `rung`, keeping the highest rank seen. */
|
|
38
|
+
export function recordDelivery(seen, realpath, rung) {
|
|
39
|
+
const rank = rungRank(rung);
|
|
40
|
+
const have = seen.get(realpath);
|
|
41
|
+
if (have === undefined || have < rank)
|
|
42
|
+
seen.set(realpath, rank);
|
|
43
|
+
}
|
|
44
|
+
/** Max-merge `source` into `target` — used to fold an out-of-process delivery
|
|
45
|
+
* (the daemon's warm-spare bearings push) into a live process's shared map. */
|
|
46
|
+
export function mergeInjectedDocs(target, source) {
|
|
47
|
+
for (const [real, rank] of source) {
|
|
48
|
+
const have = target.get(real);
|
|
49
|
+
if (have === undefined || have < rank)
|
|
50
|
+
target.set(real, rank);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Per-process cache behind `sharedInjectedDocs`: one Map instance per node,
|
|
23
54
|
* shared by every extension in the pi process (context-intro's workspace-open
|
|
24
55
|
* render and doc-substrate's on-read hook), so a doc delivered by one hook is
|
|
25
|
-
* already in the
|
|
56
|
+
* already in the map the other dedups against. */
|
|
26
57
|
const cache = new Map();
|
|
27
|
-
/** The process-shared dedup
|
|
28
|
-
* SAME
|
|
29
|
-
* consult one
|
|
58
|
+
/** The process-shared dedup map for a node: loaded from disk once, then the
|
|
59
|
+
* SAME Map instance on every call, so all hooks in this pi process grow and
|
|
60
|
+
* consult one map. Daemon-side deliveries (a warm-spare claim) write the disk
|
|
30
61
|
* file from another process; consumers that must see those merge the plain
|
|
31
62
|
* `loadInjectedDocs` result in before rendering. */
|
|
32
63
|
export function sharedInjectedDocs(nodeId) {
|
|
@@ -37,31 +68,47 @@ export function sharedInjectedDocs(nodeId) {
|
|
|
37
68
|
}
|
|
38
69
|
return seen;
|
|
39
70
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*
|
|
71
|
+
const CONTENT_RANK = rungRank('content');
|
|
72
|
+
/** Rehydrate a node's dedup map from disk. Returns an empty map when the file
|
|
73
|
+
* is absent, unreadable, or malformed (a fresh transcript, or a node that has
|
|
74
|
+
* not yet surfaced any doc). */
|
|
43
75
|
export function loadInjectedDocs(nodeId) {
|
|
44
76
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
77
|
+
const raw = JSON.parse(readFileSync(injectedDocsPath(nodeId), 'utf8'));
|
|
78
|
+
const out = new Map();
|
|
79
|
+
if (Array.isArray(raw)) {
|
|
80
|
+
// legacy v1: bare realpath array, never-re-deliver semantics
|
|
81
|
+
for (const x of raw)
|
|
82
|
+
if (typeof x === 'string')
|
|
83
|
+
out.set(x, CONTENT_RANK);
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
if (raw !== null && typeof raw === 'object') {
|
|
87
|
+
const docs = raw.docs;
|
|
88
|
+
if (docs !== null && typeof docs === 'object' && !Array.isArray(docs)) {
|
|
89
|
+
for (const [real, rank] of Object.entries(docs)) {
|
|
90
|
+
if (typeof rank === 'number' && rank >= 1 && rank <= CONTENT_RANK)
|
|
91
|
+
out.set(real, rank);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
49
96
|
}
|
|
50
97
|
catch {
|
|
51
|
-
return new
|
|
98
|
+
return new Map();
|
|
52
99
|
}
|
|
53
100
|
}
|
|
54
|
-
/** Persist a node's
|
|
55
|
-
*
|
|
101
|
+
/** Persist a node's dedup map. Called after each delivery that grew it, so
|
|
102
|
+
* the grown map survives a later dormancy. */
|
|
56
103
|
export function saveInjectedDocs(nodeId, seen) {
|
|
57
104
|
try {
|
|
58
|
-
writeFileSync(injectedDocsPath(nodeId), JSON.stringify(
|
|
105
|
+
writeFileSync(injectedDocsPath(nodeId), JSON.stringify({ v: 2, docs: Object.fromEntries(seen) }));
|
|
59
106
|
}
|
|
60
107
|
catch {
|
|
61
108
|
// best-effort — a failed persist only risks a re-inject, never a crash
|
|
62
109
|
}
|
|
63
110
|
}
|
|
64
|
-
/** Drop a node's persisted dedup
|
|
111
|
+
/** Drop a node's persisted dedup map. Called by the launch paths that start a
|
|
65
112
|
* FRESH transcript, so the new conversation surfaces docs from scratch. */
|
|
66
113
|
export function clearInjectedDocs(nodeId) {
|
|
67
114
|
cache.delete(nodeId);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MemoryDoc } from '../memory-resolver.js';
|
|
2
|
+
import { type InjectedDocs } from './injected-store.js';
|
|
3
|
+
/** Dedup key for a whole directory's listing. Realpath keys start with `/`,
|
|
4
|
+
* so the scheme prefix never collides with a doc entry. */
|
|
5
|
+
export declare function dirDedupKey(dir: string): string;
|
|
6
|
+
/** First-occurrence-wins name map over a precedence-ordered corpus: the doc
|
|
7
|
+
* that renders a name's line is the doc a read of that name would return. */
|
|
8
|
+
export declare function docsByName(corpus: readonly MemoryDoc[]): Map<string, MemoryDoc>;
|
|
9
|
+
/** Is `name` a directory in the merged name tree — i.e. does any doc live
|
|
10
|
+
* beneath it? The corpus root ('') is never a directory here: store roots
|
|
11
|
+
* merge into the whole catalog, which `memory list` owns. */
|
|
12
|
+
export declare function isDirName(byName: ReadonlyMap<string, MemoryDoc>, name: string): boolean;
|
|
13
|
+
/** The dirs a doc read discloses: the doc's own directory, then each ancestor
|
|
14
|
+
* walking up, the root excluded. `a/b/c` → `['a/b', 'a']`. */
|
|
15
|
+
export declare function ancestorDirsOf(name: string): string[];
|
|
16
|
+
/** Render one directory's listing lines: `[[name]]: <when-and-why>` per
|
|
17
|
+
* member doc (bare `[[name]]` when the doc carries no routing line), a bare
|
|
18
|
+
* `[[name]]` per subdirectory. `unlisted` members are omitted always;
|
|
19
|
+
* `filterDelivered` additionally drops members already delivered at preview
|
|
20
|
+
* rank or higher. Rendered members record at preview rank into `seen`. */
|
|
21
|
+
export declare function renderDirListing(byName: ReadonlyMap<string, MemoryDoc>, dir: string, seen: InjectedDocs | null, filterDelivered: boolean): string[];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// listings.ts — system-owned directory listings over the merged name tree.
|
|
2
|
+
//
|
|
3
|
+
// Reading a doc discloses where it sits: `ls` semantics over the corpus
|
|
4
|
+
// namespace (directories merge across stores), consulting no frontmatter
|
|
5
|
+
// except `unlisted`. A listing renders one when-and-why line per member doc
|
|
6
|
+
// and one bare name per subdirectory — never recursive contents, never gates.
|
|
7
|
+
//
|
|
8
|
+
// Two render modes share one function. An EXPLICIT directory read (`crtr
|
|
9
|
+
// memory read <dir>`) is a deliberate act, so every line renders (unlisted
|
|
10
|
+
// excluded) — mirroring how an explicit doc read always returns content. The
|
|
11
|
+
// AUTOMATIC neighborhood listings a doc read prepends are unsolicited, so
|
|
12
|
+
// each member line dedup-filters against the transcript set and whole
|
|
13
|
+
// directories dedup under a synthetic `dir://<name>` key (a name-tree
|
|
14
|
+
// directory spans stores, so no single physical realpath identifies it).
|
|
15
|
+
// Either mode records rendered member docs at preview rank — a listing line
|
|
16
|
+
// IS a preview-rank render — when a dedup map is supplied.
|
|
17
|
+
import { realpathOrSelf } from '../fs-utils.js';
|
|
18
|
+
import { parseSubstrateDoc, previewLine } from './schema.js';
|
|
19
|
+
import { deliveredAtOrAbove, recordDelivery } from './injected-store.js';
|
|
20
|
+
/** Dedup key for a whole directory's listing. Realpath keys start with `/`,
|
|
21
|
+
* so the scheme prefix never collides with a doc entry. */
|
|
22
|
+
export function dirDedupKey(dir) {
|
|
23
|
+
return `dir://${dir}`;
|
|
24
|
+
}
|
|
25
|
+
/** First-occurrence-wins name map over a precedence-ordered corpus: the doc
|
|
26
|
+
* that renders a name's line is the doc a read of that name would return. */
|
|
27
|
+
export function docsByName(corpus) {
|
|
28
|
+
const byName = new Map();
|
|
29
|
+
for (const d of corpus)
|
|
30
|
+
if (!byName.has(d.name))
|
|
31
|
+
byName.set(d.name, d);
|
|
32
|
+
return byName;
|
|
33
|
+
}
|
|
34
|
+
/** Is `name` a directory in the merged name tree — i.e. does any doc live
|
|
35
|
+
* beneath it? The corpus root ('') is never a directory here: store roots
|
|
36
|
+
* merge into the whole catalog, which `memory list` owns. */
|
|
37
|
+
export function isDirName(byName, name) {
|
|
38
|
+
if (name === '')
|
|
39
|
+
return false;
|
|
40
|
+
const prefix = name + '/';
|
|
41
|
+
for (const n of byName.keys())
|
|
42
|
+
if (n.startsWith(prefix))
|
|
43
|
+
return true;
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
/** The dirs a doc read discloses: the doc's own directory, then each ancestor
|
|
47
|
+
* walking up, the root excluded. `a/b/c` → `['a/b', 'a']`. */
|
|
48
|
+
export function ancestorDirsOf(name) {
|
|
49
|
+
const dirs = [];
|
|
50
|
+
const segs = name.split('/');
|
|
51
|
+
for (let i = segs.length - 1; i >= 1; i--)
|
|
52
|
+
dirs.push(segs.slice(0, i).join('/'));
|
|
53
|
+
return dirs;
|
|
54
|
+
}
|
|
55
|
+
/** Render one directory's listing lines: `[[name]]: <when-and-why>` per
|
|
56
|
+
* member doc (bare `[[name]]` when the doc carries no routing line), a bare
|
|
57
|
+
* `[[name]]` per subdirectory. `unlisted` members are omitted always;
|
|
58
|
+
* `filterDelivered` additionally drops members already delivered at preview
|
|
59
|
+
* rank or higher. Rendered members record at preview rank into `seen`. */
|
|
60
|
+
export function renderDirListing(byName, dir, seen, filterDelivered) {
|
|
61
|
+
const prefix = dir + '/';
|
|
62
|
+
const memberLines = new Map();
|
|
63
|
+
const subdirs = new Set();
|
|
64
|
+
for (const [name, doc] of byName) {
|
|
65
|
+
if (!name.startsWith(prefix))
|
|
66
|
+
continue;
|
|
67
|
+
const rest = name.slice(prefix.length);
|
|
68
|
+
const slash = rest.indexOf('/');
|
|
69
|
+
if (slash >= 0) {
|
|
70
|
+
subdirs.add(rest.slice(0, slash));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const sub = parseSubstrateDoc(doc);
|
|
74
|
+
if (sub?.unlisted)
|
|
75
|
+
continue;
|
|
76
|
+
const real = realpathOrSelf(doc.path);
|
|
77
|
+
if (filterDelivered && seen !== null && deliveredAtOrAbove(seen, real, 'preview'))
|
|
78
|
+
continue;
|
|
79
|
+
const line = sub === null ? '' : previewLine(sub);
|
|
80
|
+
memberLines.set(rest, line === '' ? `[[${name}]]` : `[[${name}]]: ${line}`);
|
|
81
|
+
if (seen !== null)
|
|
82
|
+
recordDelivery(seen, real, 'preview');
|
|
83
|
+
}
|
|
84
|
+
return [
|
|
85
|
+
...[...memberLines.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, l]) => l),
|
|
86
|
+
...[...subdirs].sort((a, b) => a.localeCompare(b)).map((s) => `[[${prefix}${s}]]`),
|
|
87
|
+
];
|
|
88
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import type { InjectedDocs } from './injected-store.js';
|
|
1
2
|
/** Surface docs matched by a successful read tool call. An unknown node id or
|
|
2
3
|
* an unreadable canvas renders nothing. */
|
|
3
|
-
export declare function renderOnReadDocs(nodeId: string, readFilePath: string, seen?:
|
|
4
|
+
export declare function renderOnReadDocs(nodeId: string, readFilePath: string, seen?: InjectedDocs): string;
|
|
4
5
|
/** Surface workspace-wide docs during first-message assembly, using the node's
|
|
5
|
-
* recorded cwd and selected profile
|
|
6
|
-
*
|
|
7
|
-
* read event, “a file under the store's owning dir was read”). `seen` is the
|
|
6
|
+
* recorded cwd and selected profile: docs in mounted project stores carrying
|
|
7
|
+
* a `workspace-open` surfaces entry deliver here. `seen` is the
|
|
8
8
|
* transcript-scoped dedup set shared with the on-read hook. */
|
|
9
|
-
export declare function renderWorkspaceOpenDocs(nodeId: string, seen?:
|
|
9
|
+
export declare function renderWorkspaceOpenDocs(nodeId: string, seen?: InjectedDocs): string;
|
|
@@ -8,7 +8,7 @@ import { assembleNodeSubject } from './subject.js';
|
|
|
8
8
|
import { renderOnReadDocsForSubject, renderWorkspaceOpenDocsForSubject } from './on-read.js';
|
|
9
9
|
/** Surface docs matched by a successful read tool call. An unknown node id or
|
|
10
10
|
* an unreadable canvas renders nothing. */
|
|
11
|
-
export function renderOnReadDocs(nodeId, readFilePath, seen = new
|
|
11
|
+
export function renderOnReadDocs(nodeId, readFilePath, seen = new Map()) {
|
|
12
12
|
let subject;
|
|
13
13
|
try {
|
|
14
14
|
subject = assembleNodeSubject(nodeId);
|
|
@@ -21,11 +21,10 @@ export function renderOnReadDocs(nodeId, readFilePath, seen = new Set()) {
|
|
|
21
21
|
return renderOnReadDocsForSubject(subject, readFilePath, seen);
|
|
22
22
|
}
|
|
23
23
|
/** Surface workspace-wide docs during first-message assembly, using the node's
|
|
24
|
-
* recorded cwd and selected profile
|
|
25
|
-
*
|
|
26
|
-
* read event, “a file under the store's owning dir was read”). `seen` is the
|
|
24
|
+
* recorded cwd and selected profile: docs in mounted project stores carrying
|
|
25
|
+
* a `workspace-open` surfaces entry deliver here. `seen` is the
|
|
27
26
|
* transcript-scoped dedup set shared with the on-read hook. */
|
|
28
|
-
export function renderWorkspaceOpenDocs(nodeId, seen = new
|
|
27
|
+
export function renderWorkspaceOpenDocs(nodeId, seen = new Map()) {
|
|
29
28
|
try {
|
|
30
29
|
const subject = assembleNodeSubject(nodeId);
|
|
31
30
|
const node = getNode(nodeId);
|
|
@@ -1,9 +1,30 @@
|
|
|
1
|
+
import { type InjectedDocs } from './injected-store.js';
|
|
2
|
+
import { type Rung, type SubstrateDoc } from './schema.js';
|
|
1
3
|
import type { NodeConfigSubject } from './subject-fields.js';
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
|
|
4
|
+
/** Render candidates at their matched rungs, gate-checked and deduped against
|
|
5
|
+
* the transcript set (higher rungs pierce lower; a delivery records at its
|
|
6
|
+
* rendered rung). A `null` subject means no node identity is available for
|
|
7
|
+
* gate evaluation: gated docs are skipped, ungated docs still deliver. */
|
|
8
|
+
export declare function renderCandidateBlocks(subject: NodeConfigSubject | null, candidates: Array<{
|
|
9
|
+
doc: SubstrateDoc;
|
|
10
|
+
rung: Rung;
|
|
11
|
+
}>, seen: InjectedDocs): string[];
|
|
12
|
+
/** Surface docs whose `read` entries match a successfully read file, using
|
|
13
|
+
* the caller-supplied daemon snapshot subject. */
|
|
14
|
+
export declare function renderOnReadDocsForSubject(subject: NodeConfigSubject, readFilePath: string, seen?: InjectedDocs): string;
|
|
5
15
|
/** Workspace boot docs using the snapshot's cwd/profile instead of a canvas
|
|
6
16
|
* lookup. `seen` is the transcript-scoped dedup set shared with the on-read
|
|
7
17
|
* hook, so a front door delivered here never re-delivers on a later read
|
|
8
18
|
* beneath its store. */
|
|
9
|
-
export declare function renderWorkspaceOpenDocsForSubject(subject: NodeConfigSubject, cwd: string, profileId: string | null, seen?:
|
|
19
|
+
export declare function renderWorkspaceOpenDocsForSubject(subject: NodeConfigSubject, cwd: string, profileId: string | null, seen?: InjectedDocs): string;
|
|
20
|
+
/** Inner `<memory>` blocks for the memory-read event: docs whose
|
|
21
|
+
* `memory-read` entries match the doc a `crtr memory read` just resolved
|
|
22
|
+
* (known by `targetNames`; `excludeRealpath` keeps the resolved doc from
|
|
23
|
+
* delivering itself). Returns unwrapped blocks — the read leaf composes one
|
|
24
|
+
* `<auto-loaded-context>` envelope from these plus the directory listings. */
|
|
25
|
+
export declare function memoryReadDocBlocks(subject: NodeConfigSubject | null, excludeRealpath: string, targetNames: string[], seen: InjectedDocs): string[];
|
|
26
|
+
/** Surface docs whose `command` entries match an executed bash command
|
|
27
|
+
* string. The corpus is the resolved cwd/profile set only — a command has no
|
|
28
|
+
* file to walk enclosing stores from. `seen` is the same transcript-scoped
|
|
29
|
+
* dedup set as the other channels. */
|
|
30
|
+
export declare function renderOnCommandDocsForSubject(subject: NodeConfigSubject, command: string, seen?: InjectedDocs): string;
|