@north-light/crouter 0.3.207 → 0.3.209
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/__tests__/group-activity.test.js +8 -1
- package/dist/clients/attach/render/card-presentation.d.ts +31 -0
- package/dist/clients/attach/render/card-presentation.js +163 -0
- package/dist/clients/attach/render/chat-view.d.ts +0 -2
- package/dist/clients/attach/render/chat-view.js +12 -23
- package/dist/clients/attach/render/context-message.d.ts +5 -9
- package/dist/clients/attach/render/context-message.js +20 -43
- package/dist/clients/attach/render/group-activity.d.ts +28 -4
- package/dist/clients/attach/render/group-activity.js +65 -11
- package/dist/clients/attach/render/group-recap.d.ts +3 -0
- package/dist/clients/attach/render/group-recap.js +17 -0
- package/dist/clients/attach/viewer.js +516 -516
- 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__/canvas-inbox-watcher.test.js +1 -1
- package/dist/core/__tests__/helpers/broker-clients.d.ts +3 -1
- package/dist/core/__tests__/helpers/broker-clients.js +7 -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/__tests__/seam/broker-attach-multiclient.test.js +9 -0
- package/dist/core/__tests__/serial/broker-snapshot-history.test.js +52 -1
- package/dist/core/__tests__/serial/flagship-lifecycle.test.js +1 -1
- 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/fault-retry.js +2 -7
- package/dist/core/runtime/broker/frame-dispatch.js +1 -1
- package/dist/core/runtime/broker/tool-groups.js +7 -6
- 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/broker.js +4 -4
- package/dist/core/runtime/kickoff.js +4 -10
- package/dist/core/runtime/memory.js +2 -3
- package/dist/core/runtime/node-read.js +7 -1
- package/dist/core/runtime/stop-guard.js +3 -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/daemon/review/comment-notify.js +1 -8
- package/dist/daemon/review/deliver.js +1 -1
- package/dist/daemon/review/finish.js +1 -1
- 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/__tests__/canvas-stophook-agentend.test.js +1 -1
- 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/dist/pi-extensions/canvas-review-boundary.js +1 -1
- package/dist/shared/__tests__/generated-context-grammar.test.js +19 -22
- package/dist/shared/generated-context.d.ts +0 -18
- package/dist/shared/generated-context.js +16 -120
- package/dist/shared/tool-groups.d.ts +1 -0
- package/dist/shared/tool-groups.js +7 -0
- 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
|
@@ -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;
|
|
@@ -1,45 +1,41 @@
|
|
|
1
|
-
// on-read.ts — the pure
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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
|
|
8
|
-
//
|
|
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
|
-
// •
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
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
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
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 {
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
|
187
|
+
for (const { doc, rung } of candidates) {
|
|
237
188
|
const real = realpathOrSelf(doc.path);
|
|
238
|
-
if (seen
|
|
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
|
|
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
|
|
258
|
-
* daemon snapshot subject. */
|
|
259
|
-
export function renderOnReadDocsForSubject(subject, readFilePath, seen = new
|
|
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
|
|
217
|
+
const candidates = dedupeByPhysicalPath([...enclosingProjectDocs(absReadFile), ...resolvedDocs()])
|
|
263
218
|
.filter((doc) => realpathOrSelf(doc.path) !== absReadFile)
|
|
264
|
-
.
|
|
265
|
-
|
|
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
|
|
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
|
-
|
|
282
|
-
.
|
|
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,
|
|
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
|
-
|
|
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;
|