@north-light/crouter 0.3.221 → 0.3.223
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 +21 -1
- package/dist/api/client.js +34 -0
- package/dist/api/dto/chat-inventory.d.ts +13 -0
- package/dist/api/dto/human-requests.d.ts +88 -0
- package/dist/api/dto/human-requests.js +4 -0
- package/dist/api/dto/human.d.ts +3 -0
- package/dist/api/dto/reviews.d.ts +2 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/routes.d.ts +7 -0
- package/dist/api/routes.js +10 -0
- package/dist/builtin-memory/00-runtime-base/00-authoring.md +31 -0
- package/dist/builtin-memory/00-runtime-base/01-escalation.md +14 -0
- package/dist/builtin-memory/{insights/listen.md → 00-runtime-base/02-insight-capture.md} +1 -0
- package/dist/builtin-memory/02-turn-lifecycle/00-ending-a-turn.md +27 -0
- package/dist/builtin-memory/{02-lifecycle/01-resident.md → 02-turn-lifecycle/02-resident.md} +5 -0
- package/dist/builtin-memory/04-base-worker.md +4 -8
- package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
- package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +1 -0
- package/dist/builtin-memory/05-kinds/design/00-base.md +2 -1
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +2 -1
- package/dist/builtin-memory/05-kinds/design/design-contract.md +19 -0
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/general/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/00-base.md +2 -1
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +2 -1
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +28 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/lens-contract.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -0
- package/dist/builtin-memory/05-kinds/review/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/review/security-findings.md +1 -0
- package/dist/builtin-memory/05-kinds/spec/00-base.md +4 -3
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/spec/requirements.md +1 -0
- package/dist/builtin-memory/design/guide.md +35 -0
- package/dist/builtin-memory/design/roadmap.md +21 -0
- package/dist/builtin-memory/insights/capture.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +4 -4
- package/dist/builtin-memory/internal/plugins.md +10 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/plan/roadmap.md +6 -28
- package/dist/builtin-memory/spec/guide.md +19 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +28 -15
- package/dist/clients/attach/render/markdown-source.js +106 -1
- package/dist/clients/attach/session/file-links.d.ts +13 -4
- package/dist/clients/attach/session/file-links.js +54 -58
- package/dist/clients/attach/viewer.js +545 -543
- package/dist/clients/inbox/controller.js +1 -1
- package/dist/clients/inbox/page-adapter.js +2 -1
- package/dist/clients/inbox/resolve.d.ts +1 -0
- package/dist/clients/inbox/review/review-client.js +3 -1
- package/dist/clients/inbox/tui/panel.js +23 -4
- package/dist/clients/inbox/tui/render.js +6 -0
- package/dist/clients/inbox/tui/types.d.ts +8 -0
- package/dist/commands/__tests__/human.test.js +2 -2
- package/dist/commands/human/request.d.ts +2 -0
- package/dist/commands/human/request.js +281 -0
- package/dist/commands/human.js +5 -2
- package/dist/commands/memory/shared.d.ts +2 -2
- package/dist/commands/memory/shared.js +11 -6
- package/dist/commands/pkg/browse/doc-view.js +2 -0
- package/dist/commands/sys/config.js +2 -2
- package/dist/commands/sys/doctor.js +54 -2
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +7 -4
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.d.ts +1 -0
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +71 -0
- package/dist/core/__tests__/human-action-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/human-action-delivery.test.js +140 -0
- package/dist/core/__tests__/human-actions.test.d.ts +1 -0
- package/dist/core/__tests__/human-actions.test.js +116 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +1 -1
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +70 -3
- package/dist/core/__tests__/prospective-inventory-capability-parity.test.d.ts +1 -0
- package/dist/core/__tests__/prospective-inventory-capability-parity.test.js +91 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.d.ts +1 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +127 -0
- package/dist/core/__tests__/seam/prospective-inventory-stdout.test.d.ts +1 -0
- package/dist/core/__tests__/seam/prospective-inventory-stdout.test.js +31 -0
- package/dist/core/canvas/db.js +23 -0
- package/dist/core/canvas/human-deliveries.d.ts +53 -0
- package/dist/core/canvas/human-deliveries.js +75 -0
- package/dist/core/config.d.ts +13 -1
- package/dist/core/config.js +51 -1
- package/dist/core/feed/inbox.d.ts +6 -0
- package/dist/core/feed/inbox.js +9 -1
- package/dist/core/human/action-binding.d.ts +21 -0
- package/dist/core/human/action-binding.js +40 -0
- package/dist/core/human/completion.d.ts +38 -0
- package/dist/core/human/completion.js +27 -0
- package/dist/core/human/convention.d.ts +2 -0
- package/dist/core/human/convention.js +2 -0
- package/dist/core/human/tickets.d.ts +25 -6
- package/dist/core/human/tickets.js +19 -13
- package/dist/core/human/types.d.ts +5 -0
- package/dist/core/human-actions.d.ts +25 -0
- package/dist/core/human-actions.js +101 -0
- package/dist/core/memory-resolver.js +1 -1
- package/dist/core/profiles/select.d.ts +2 -0
- package/dist/core/profiles/select.js +21 -4
- package/dist/core/runtime/broker/frame-dispatch.js +2 -5
- package/dist/core/runtime/broker-inventory.d.ts +1 -2
- package/dist/core/runtime/broker-inventory.js +2 -77
- package/dist/core/runtime/broker-persona-guidance.js +1 -1
- package/dist/core/runtime/broker.js +4 -4
- package/dist/core/runtime/chat-inventory-rows.d.ts +8 -0
- package/dist/core/runtime/chat-inventory-rows.js +105 -0
- package/dist/core/runtime/command-surface.d.ts +8 -3
- package/dist/core/runtime/command-surface.js +42 -6
- package/dist/core/runtime/launch-target.d.ts +25 -0
- package/dist/core/runtime/launch-target.js +54 -0
- package/dist/core/runtime/persona.js +3 -3
- package/dist/core/runtime/prospective-inventory-cli.d.ts +1 -0
- package/dist/core/runtime/prospective-inventory-cli.js +61 -0
- package/dist/core/runtime/prospective-inventory.d.ts +10 -0
- package/dist/core/runtime/prospective-inventory.js +88 -0
- package/dist/core/runtime/spawn.d.ts +3 -1
- package/dist/core/runtime/spawn.js +5 -3
- package/dist/core/substrate/frontmatter-validation.d.ts +2 -5
- package/dist/core/substrate/frontmatter-validation.js +8 -4
- package/dist/core/substrate/gate.d.ts +4 -1
- package/dist/core/substrate/gate.js +5 -0
- package/dist/core/substrate/on-read.js +21 -32
- package/dist/core/substrate/render-node.d.ts +3 -2
- package/dist/core/substrate/render-node.js +3 -2
- package/dist/core/substrate/render.js +64 -37
- package/dist/core/substrate/schema.d.ts +17 -8
- package/dist/core/substrate/schema.js +14 -10
- package/dist/core/substrate/surface-match.d.ts +8 -7
- package/dist/core/substrate/surface-match.js +15 -14
- package/dist/core/user-settings.d.ts +4 -0
- package/dist/core/user-settings.js +1 -0
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +52 -3
- package/dist/daemon/api/handlers/human-requests.d.ts +2 -0
- package/dist/daemon/api/handlers/human-requests.js +409 -0
- package/dist/daemon/api/handlers/human.js +3 -0
- package/dist/daemon/api/handlers/inbox.js +3 -0
- package/dist/daemon/api/handlers/nodes.d.ts +1 -3
- package/dist/daemon/api/handlers/nodes.js +11 -46
- package/dist/daemon/api/handlers/prospective-chat-inventory.d.ts +2 -0
- package/dist/daemon/api/handlers/prospective-chat-inventory.js +59 -0
- package/dist/daemon/api/handlers/reviews.js +10 -2
- package/dist/daemon/api/server.js +4 -0
- package/dist/daemon/crtrd.js +6 -0
- package/dist/daemon/human/deliver-action.d.ts +16 -0
- package/dist/daemon/human/deliver-action.js +168 -0
- package/dist/daemon/human/finish.d.ts +8 -5
- package/dist/daemon/human/finish.js +45 -6
- package/dist/daemon/human/sweep.js +4 -1
- package/dist/daemon/reconcilers/human-delivery-lane.d.ts +10 -0
- package/dist/daemon/reconcilers/human-delivery-lane.js +41 -0
- package/dist/daemon/review/finish.d.ts +8 -3
- package/dist/daemon/review/finish.js +19 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.js +1 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/builtin-memory/00-runtime-base.md +0 -55
- package/dist/builtin-memory/design.md +0 -55
- /package/dist/builtin-memory/{02-lifecycle/00-terminal.md → 02-turn-lifecycle/01-terminal.md} +0 -0
|
@@ -48,27 +48,6 @@ function attr(s) {
|
|
|
48
48
|
.replace(/</g, '<')
|
|
49
49
|
.replace(/>/g, '>');
|
|
50
50
|
}
|
|
51
|
-
function gitRootOf(p) {
|
|
52
|
-
let d = p;
|
|
53
|
-
const root = parse(d).root;
|
|
54
|
-
while (true) {
|
|
55
|
-
if (pathExists(`${d}${sep}.git`))
|
|
56
|
-
return d;
|
|
57
|
-
if (d === root)
|
|
58
|
-
return null;
|
|
59
|
-
const parent = dirname(d);
|
|
60
|
-
if (parent === d)
|
|
61
|
-
return null;
|
|
62
|
-
d = parent;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function disp(p) {
|
|
66
|
-
const root = gitRootOf(p);
|
|
67
|
-
if (!root)
|
|
68
|
-
return p;
|
|
69
|
-
const rel = relative(root, p);
|
|
70
|
-
return rel === '' ? '.' : rel;
|
|
71
|
-
}
|
|
72
51
|
function isJunkAncestor(dir) {
|
|
73
52
|
return dir.split(sep).some((segment) => JUNK_DIRS.has(segment));
|
|
74
53
|
}
|
|
@@ -184,16 +163,26 @@ function docContentKey(doc) {
|
|
|
184
163
|
return null;
|
|
185
164
|
return `content-sha256:${createHash('sha256').update(body).digest('hex')}`;
|
|
186
165
|
}
|
|
166
|
+
// Two shapes: element-with-body means the content is here; self-closing means
|
|
167
|
+
// it is not — a preview carries its routing line as `readWhen` metadata so the
|
|
168
|
+
// one-liner can never be mistaken for the document's content. No disk path:
|
|
169
|
+
// agents address memory by name through the command surface, and the explicit
|
|
170
|
+
// read leaf already returns `path`.
|
|
187
171
|
function renderDocEnvelope(doc, rung) {
|
|
188
172
|
if (rung === 'none')
|
|
189
173
|
return null;
|
|
190
|
-
let
|
|
191
|
-
if (rung === 'content')
|
|
192
|
-
body = doc.body.trim();
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
174
|
+
let attrs = `kind="${attr(doc.kind)}" name="${attr(doc.name)}"`;
|
|
175
|
+
if (rung === 'content') {
|
|
176
|
+
const body = doc.body.trim();
|
|
177
|
+
if (body !== '')
|
|
178
|
+
return `<memory ${attrs}>\n${body}\n</memory>`;
|
|
179
|
+
}
|
|
180
|
+
else if (rung === 'preview') {
|
|
181
|
+
const pl = previewLine(doc);
|
|
182
|
+
if (pl !== '')
|
|
183
|
+
attrs += ` readWhen="${attr(pl)}"`;
|
|
184
|
+
}
|
|
185
|
+
return `<memory ${attrs} />`;
|
|
197
186
|
}
|
|
198
187
|
/** Render candidates at their matched rungs, gate-checked and deduped against
|
|
199
188
|
* the transcript set (higher rungs pierce lower; a delivery records at its
|
|
@@ -240,7 +229,7 @@ export function renderOnReadDocsForSubject(subject, readFilePath, seen = new Map
|
|
|
240
229
|
const readFrontmatter = readFileFrontmatter(absReadFile);
|
|
241
230
|
const candidates = dedupeByPhysicalPath([...enclosingProjectDocs(absReadFile), ...resolvedDocs()])
|
|
242
231
|
.filter((doc) => realpathOrSelf(doc.path) !== absReadFile)
|
|
243
|
-
.map((doc) => ({ doc, rung: readDeliveryRung(doc, absReadFile, readFrontmatter) }))
|
|
232
|
+
.map((doc) => ({ doc, rung: readDeliveryRung(doc, subject, absReadFile, readFrontmatter) }))
|
|
244
233
|
.filter(({ rung }) => rung !== 'none');
|
|
245
234
|
return renderCandidates(subject, candidates, seen);
|
|
246
235
|
}
|
|
@@ -265,7 +254,7 @@ export function renderWorkspaceOpenDocsForSubject(subject, cwd, profileId, seen
|
|
|
265
254
|
return '';
|
|
266
255
|
}
|
|
267
256
|
const candidates = dedupeByPhysicalPath(docs)
|
|
268
|
-
.map((doc) => ({ doc, rung: minRung(workspaceOpenRung(doc), doc.projectMemory) }))
|
|
257
|
+
.map((doc) => ({ doc, rung: minRung(workspaceOpenRung(doc, subject), doc.projectMemory) }))
|
|
269
258
|
.filter(({ rung }) => rung !== 'none')
|
|
270
259
|
.sort((a, b) => {
|
|
271
260
|
const aRoot = owningRootOf(a.doc) ?? '';
|
|
@@ -283,7 +272,7 @@ export function renderWorkspaceOpenDocsForSubject(subject, cwd, profileId, seen
|
|
|
283
272
|
export function memoryReadDocBlocks(subject, excludeRealpath, targetNames, seen) {
|
|
284
273
|
const candidates = dedupeByPhysicalPath(resolvedDocs())
|
|
285
274
|
.filter((doc) => realpathOrSelf(doc.path) !== excludeRealpath)
|
|
286
|
-
.map((doc) => ({ doc, rung: memoryReadDeliveryRung(doc, targetNames) }))
|
|
275
|
+
.map((doc) => ({ doc, rung: memoryReadDeliveryRung(doc, subject, targetNames) }))
|
|
287
276
|
.filter(({ rung }) => rung !== 'none');
|
|
288
277
|
return renderCandidateBlocks(subject, candidates, seen);
|
|
289
278
|
}
|
|
@@ -293,7 +282,7 @@ export function memoryReadDocBlocks(subject, excludeRealpath, targetNames, seen)
|
|
|
293
282
|
* dedup set as the other channels. */
|
|
294
283
|
export function renderOnCommandDocsForSubject(subject, command, seen = new Map()) {
|
|
295
284
|
const candidates = dedupeByPhysicalPath(resolvedDocs())
|
|
296
|
-
.map((doc) => ({ doc, rung: commandDeliveryRung(doc, command) }))
|
|
285
|
+
.map((doc) => ({ doc, rung: commandDeliveryRung(doc, subject, command) }))
|
|
297
286
|
.filter(({ rung }) => rung !== 'none');
|
|
298
287
|
return renderCandidates(subject, candidates, seen);
|
|
299
288
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { InjectedDocs } from './injected-store.js';
|
|
2
|
-
/** The system-prompt
|
|
3
|
-
*
|
|
2
|
+
/** The system-prompt substrate splice for `nodeId` — shipped-layer protocol
|
|
3
|
+
* prose followed by the `<memory kind="preference">` block: assemble
|
|
4
|
+
* the node's subject from the canvas-db, render the pure splice, then swap the
|
|
4
5
|
* literal `$CRTR_CONTEXT_DIR` env-var token for the node's real absolute path
|
|
5
6
|
* so agents don't paste it into the non-expanding Write tool (see
|
|
6
7
|
* interpolateNodePaths). A null subject (unknown id) still renders the
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
import { interpolateNodePaths } from '../canvas/paths.js';
|
|
9
9
|
import { assembleNodeSubject } from './subject.js';
|
|
10
10
|
import { renderPreferencesForSubject, renderKnowledgeForSubject } from './render.js';
|
|
11
|
-
/** The system-prompt
|
|
12
|
-
*
|
|
11
|
+
/** The system-prompt substrate splice for `nodeId` — shipped-layer protocol
|
|
12
|
+
* prose followed by the `<memory kind="preference">` block: assemble
|
|
13
|
+
* the node's subject from the canvas-db, render the pure splice, then swap the
|
|
13
14
|
* literal `$CRTR_CONTEXT_DIR` env-var token for the node's real absolute path
|
|
14
15
|
* so agents don't paste it into the non-expanding Write tool (see
|
|
15
16
|
* interpolateNodePaths). A null subject (unknown id) still renders the
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
// then the `preview`+`name` docs render as ONE catalog tree, each at its own
|
|
9
9
|
// rung (preview → a `# read when:` routing line; name → a bare entry); a doc
|
|
10
10
|
// with NO boot entry leaks only into its dir's `[+N more]` count, never a
|
|
11
|
-
// name — EXCEPT a node-local doc
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// doc outright.
|
|
11
|
+
// name — EXCEPT a node-local doc with no boot entry: renderKnowledgeForSubject
|
|
12
|
+
// floors it up to `name` so it still shows its bare name rather than
|
|
13
|
+
// collapsing into a hidden count. A node-local doc with boot entries remains
|
|
14
|
+
// absent when none of those entries participates.
|
|
16
15
|
//
|
|
17
16
|
// • the SYSTEM-PROMPT half — `<memory kind="preference">`
|
|
18
17
|
// (renderPreferencesSection) — the always-present memory-usage guidance plus
|
|
@@ -27,11 +26,11 @@
|
|
|
27
26
|
//
|
|
28
27
|
// The pipeline is explicitly TWO-STEP and must never run in the other order:
|
|
29
28
|
// 1. SELECT WINNERS — MemoryDoc → parseSubstrateDoc → (null-filter
|
|
30
|
-
// non-substrate) → per-doc boot rung (`bootRung`, the max `at` over
|
|
31
|
-
//
|
|
29
|
+
// non-substrate) → per-doc boot rung (`bootRung`, the max `at` over
|
|
30
|
+
// participating `on: boot` surfaces entries) → gatePasses → first-wins DEDUP by
|
|
32
31
|
// name over the resolver's precedence order (nearest project > user >
|
|
33
32
|
// builtin). Node-local docs follow the same gate rule, but the
|
|
34
|
-
// knowledge-block render floors a
|
|
33
|
+
// knowledge-block render floors only a node-local doc with no boot entry to
|
|
35
34
|
// `name` — the one rung-floor exemption, scoped to the node-local store
|
|
36
35
|
// only.
|
|
37
36
|
// 2. RENDER IN DISPLAY ORDER — split winners into content vs. the rest;
|
|
@@ -108,7 +107,7 @@ function selectWinners(subject, kind) {
|
|
|
108
107
|
.filter((d) => d.kind === kind)
|
|
109
108
|
.filter((d) => d.projectMemory !== 'none')
|
|
110
109
|
.filter((d) => gatePasses(d, subject))
|
|
111
|
-
.map((d) => ({ ...d, bootRung: minRung(bootRung(d), d.projectMemory) }));
|
|
110
|
+
.map((d) => ({ ...d, bootRung: minRung(bootRung(d, subject), d.projectMemory) }));
|
|
112
111
|
return dedupeFirstWins(eligible);
|
|
113
112
|
}
|
|
114
113
|
// ---------------------------------------------------------------------------
|
|
@@ -183,9 +182,16 @@ function compareTreePosition(a, b) {
|
|
|
183
182
|
}
|
|
184
183
|
return as.length - bs.length;
|
|
185
184
|
}
|
|
185
|
+
/** A doc the running agent can revise through `crtr memory edit`: any native
|
|
186
|
+
* scope-store doc (user/profile/project/node). Shipped docs — builtin and
|
|
187
|
+
* plugin-mounted — are not editable from a node; the preference render treats
|
|
188
|
+
* them as operating protocol, not memory (see buildPreferencesBlock). */
|
|
189
|
+
function isAgentEditable(doc) {
|
|
190
|
+
return doc.scope !== 'builtin' && doc.plugin === undefined;
|
|
191
|
+
}
|
|
186
192
|
/** STEP 2 — split deduped `winners` by rung and render each group in its
|
|
187
193
|
* display order: `content` docs first, as clean prose (general→specific,
|
|
188
|
-
* then tree position, then filename — no tree chrome
|
|
194
|
+
* then tree position, then filename — no tree chrome); every
|
|
189
195
|
* other winner (preview/name/hidden-bootless) folds into ONE catalog tree
|
|
190
196
|
* exactly as before. `nodeLocalSet` marks which winners came from the
|
|
191
197
|
* node-local store (ranked most-specific in content ordering); omit it for a
|
|
@@ -216,13 +222,9 @@ function renderGrouped(winners, rootLabel, nodeLocalSet = new Set()) {
|
|
|
216
222
|
* Returned across ALL kinds — node-local is the catch-all this-node store and
|
|
217
223
|
* rides into the knowledge block.
|
|
218
224
|
*
|
|
219
|
-
* Node-local docs follow the same GATE rule as every other store
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* so a doc with no boot entry is retained by this function — it is the caller
|
|
223
|
-
* (renderKnowledgeForSubject) that floors a bootless node-local doc up to
|
|
224
|
-
* `name` so it still shows its bare name instead of collapsing into a
|
|
225
|
-
* `[+N more]` count. */
|
|
225
|
+
* Node-local docs follow the same GATE rule as every other store. A doc with
|
|
226
|
+
* no boot entry is retained here; renderKnowledgeForSubject floors it to
|
|
227
|
+
* `name` so it shows as a bare name instead of collapsing into `[+N more]`. */
|
|
226
228
|
function nodeLocalDocs(nodeId, subject) {
|
|
227
229
|
const dir = memoryDir(nodeId);
|
|
228
230
|
if (!pathExists(dir))
|
|
@@ -489,37 +491,62 @@ function buildSubPersonaMenu(kind) {
|
|
|
489
491
|
lines.join('\n'));
|
|
490
492
|
}
|
|
491
493
|
// ---------------------------------------------------------------------------
|
|
492
|
-
// 1. Preferences — `<memory kind="preference">` (system prompt).
|
|
494
|
+
// 1. Preferences — protocol prose + `<memory kind="preference">` (system prompt).
|
|
493
495
|
// ---------------------------------------------------------------------------
|
|
494
|
-
/** The system-prompt
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
496
|
+
/** The system-prompt substrate splice, TWO parts in one string.
|
|
497
|
+
*
|
|
498
|
+
* 1. The operating protocol: shipped (builtin/plugin) content-rung bodies as
|
|
499
|
+
* bare prose, plus the sub-persona menu. No memory envelope — the protocol
|
|
500
|
+
* is the rules of the node's world, and wrapping it as recalled "memory:
|
|
501
|
+
* preference" content gave it softer epistemic status than the
|
|
502
|
+
* instructions it is; the intro/outro sentences ("how the user wants you
|
|
503
|
+
* to behave", "update the preference") were also false of it.
|
|
504
|
+
* 2. The `<memory kind="preference">` block: the always-present memory-usage
|
|
505
|
+
* guidance, then (when any are eligible) each agent-editable doc wrapped
|
|
506
|
+
* `<preference name="…">…</preference>` — the name is the edit handle the
|
|
507
|
+
* outro's update directive needs — then the preview/name catalog tree.
|
|
508
|
+
* Every frame sentence is true of everything inside it.
|
|
509
|
+
*
|
|
510
|
+
* Ordering across the two parts is unchanged from the single-block render
|
|
511
|
+
* (shipped→editable was already the scope sort), so the shared cache prefix
|
|
512
|
+
* across nodes is preserved. ALWAYS returns a non-empty splice — the guidance
|
|
513
|
+
* frame is unconditional, so it is never empty even when no preference is
|
|
514
|
+
* eligible. */
|
|
500
515
|
function buildPreferencesBlock(subject, seen) {
|
|
501
|
-
let body = MEMORY_USAGE_GUIDANCE;
|
|
502
516
|
let contentDocs = [];
|
|
503
517
|
let tree = '';
|
|
518
|
+
let protocol = '';
|
|
519
|
+
let memBody = MEMORY_USAGE_GUIDANCE;
|
|
504
520
|
if (subject !== null) {
|
|
505
521
|
const winners = selectWinners(subject, 'preference');
|
|
506
522
|
seedBootDelivery(seen, winners);
|
|
507
523
|
const grouped = renderGrouped(winners, 'preferences');
|
|
508
524
|
contentDocs = grouped.contentDocs;
|
|
509
525
|
tree = grouped.tree;
|
|
526
|
+
const shipped = contentDocs
|
|
527
|
+
.filter((d) => !isAgentEditable(d))
|
|
528
|
+
.map((d) => d.body.trim())
|
|
529
|
+
.filter((b) => b !== '');
|
|
530
|
+
const editable = contentDocs
|
|
531
|
+
.filter((d) => isAgentEditable(d))
|
|
532
|
+
.map((d) => {
|
|
533
|
+
const b = d.body.trim();
|
|
534
|
+
return b === '' ? '' : `<preference name="${d.name}">\n${b}\n</preference>`;
|
|
535
|
+
})
|
|
536
|
+
.filter((b) => b !== '');
|
|
510
537
|
const subPersonaMenu = buildSubPersonaMenu(subject.kind);
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
body += `\n\n${PREFERENCES_OUTRO}`;
|
|
538
|
+
protocol = [...shipped, ...(subPersonaMenu === '' ? [] : [subPersonaMenu])].join('\n\n');
|
|
539
|
+
if (editable.length > 0 || tree !== '') {
|
|
540
|
+
memBody += `\n\n${PREFERENCES_INTRO}`;
|
|
541
|
+
if (editable.length > 0)
|
|
542
|
+
memBody += `\n\n${editable.join('\n\n')}`;
|
|
543
|
+
if (tree !== '')
|
|
544
|
+
memBody += `\n\n${tree}`;
|
|
545
|
+
memBody += `\n\n${PREFERENCES_OUTRO}`;
|
|
520
546
|
}
|
|
521
547
|
}
|
|
522
|
-
|
|
548
|
+
const memoryBlock = `<memory kind="preference">\n${memBody}\n</memory>`;
|
|
549
|
+
return { block: protocol === '' ? memoryBlock : `${protocol}\n\n${memoryBlock}`, contentDocs, tree };
|
|
523
550
|
}
|
|
524
551
|
export function renderPreferencesForSubject(subject, seen) {
|
|
525
552
|
return buildPreferencesBlock(subject, seen);
|
|
@@ -542,8 +569,8 @@ export function renderPreferencesForSubject(subject, seen) {
|
|
|
542
569
|
* eligible. */
|
|
543
570
|
export function renderKnowledgeForSubject(subject, nodeId, seen) {
|
|
544
571
|
const nodeLocal = nodeLocalDocs(nodeId, subject).map((d) => {
|
|
545
|
-
const r = bootRung(d);
|
|
546
|
-
return { ...d, bootRung: isVisible(r) ? r : 'name' };
|
|
572
|
+
const r = bootRung(d, subject);
|
|
573
|
+
return { ...d, bootRung: isVisible(r) || d.surfaces.some((e) => e.on === 'boot') ? r : 'name' };
|
|
547
574
|
});
|
|
548
575
|
const nodeLocalSet = new Set(nodeLocal);
|
|
549
576
|
// Resolver winners first: first-wins dedup keeps the resolver's copy over a
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ProfileProjectMemory } from '../../api/dto/profiles.js';
|
|
2
2
|
import type { MemoryDoc, MemoryScope } from '../memory-resolver.js';
|
|
3
|
+
import type { NodeConfigSubject } from './subject-fields.js';
|
|
3
4
|
export declare const KINDS: readonly ["knowledge", "preference"];
|
|
4
5
|
export type DocKind = (typeof KINDS)[number];
|
|
5
6
|
/** Is `v` one of the two valid document kinds? */
|
|
@@ -14,7 +15,7 @@ export declare function rungAtLeast(r: Rung, min: Rung): boolean;
|
|
|
14
15
|
* authored rung against its project relationship's cap. */
|
|
15
16
|
export declare function minRung(a: Rung, b: Rung): Rung;
|
|
16
17
|
/** Strip an optional `NN-` ordering prefix from ONE path segment (file or
|
|
17
|
-
* directory display name). `00-runtime-base` -> `runtime-base`; `spine` (no
|
|
18
|
+
* directory display name). `00-runtime-base/00-authoring` -> `runtime-base/authoring`; `spine` (no
|
|
18
19
|
* prefix) is unchanged. */
|
|
19
20
|
export declare function normalizeNameSegment(segment: string): string;
|
|
20
21
|
/** Normalize a full slash-separated path-derived doc name by stripping the
|
|
@@ -34,10 +35,10 @@ export type SurfaceEvent = (typeof SURFACE_EVENTS)[number];
|
|
|
34
35
|
export declare const SURFACE_RUNGS: readonly ["name", "preview", "content"];
|
|
35
36
|
export type SurfaceRung = (typeof SURFACE_RUNGS)[number];
|
|
36
37
|
/** One routing entry: when event `on` exposes a subject that fits `match`
|
|
37
|
-
* (and `matchFrontmatter`, `read` only)
|
|
38
|
-
* Constraints within one entry AND together
|
|
39
|
-
* fit; a `match` list is satisfied by any one
|
|
40
|
-
* event OR together. */
|
|
38
|
+
* (and `matchFrontmatter`, `read` only) and its optional node-config `gate`,
|
|
39
|
+
* the doc delivers at rung `at`. Constraints within one entry AND together
|
|
40
|
+
* (each present constraint must fit; a `match` list is satisfied by any one
|
|
41
|
+
* glob); multiple entries per event OR together. */
|
|
41
42
|
export interface SurfaceEntry {
|
|
42
43
|
on: SurfaceEvent;
|
|
43
44
|
/** Globs vs the event's subject namespace. Required on read/memory-read/
|
|
@@ -47,11 +48,15 @@ export interface SurfaceEntry {
|
|
|
47
48
|
/** Predicate over the read file's own YAML frontmatter — `read` event only.
|
|
48
49
|
* Frontmatter key `match-frontmatter`. */
|
|
49
50
|
matchFrontmatter?: GatePredicate;
|
|
51
|
+
/** Optional predicate over the current node config. Frontmatter key `gate`.
|
|
52
|
+
* The document-level gate remains the hard eligibility check. */
|
|
53
|
+
gate?: GatePredicate;
|
|
50
54
|
at: SurfaceRung;
|
|
51
55
|
}
|
|
52
|
-
/** A doc's boot rung
|
|
53
|
-
* it carries
|
|
54
|
-
|
|
56
|
+
/** A doc's boot rung for `subject`: the highest `at` over participating
|
|
57
|
+
* `boot` entries, `none` when it carries none. The boot render's per-doc
|
|
58
|
+
* selection input. */
|
|
59
|
+
export declare function bootRung(doc: Pick<SubstrateSchema, 'surfaces'>, subject: NodeConfigSubject): Rung;
|
|
55
60
|
/** A gate predicate tree, evaluated by predicate.ts (`evalCondition`) against
|
|
56
61
|
* the node-config subject. Typed loosely on purpose — the matcher engine owns
|
|
57
62
|
* validation; structurally it is a field→matcher map with optional
|
|
@@ -111,6 +116,10 @@ export interface SubstrateDoc extends SubstrateSchema {
|
|
|
111
116
|
name: string;
|
|
112
117
|
/** The scope this doc resolved from. */
|
|
113
118
|
scope: MemoryScope;
|
|
119
|
+
/** Owning plugin name when the doc is mounted from an installed plugin;
|
|
120
|
+
* undefined for a native scope doc. Shipped (builtin/plugin) docs and
|
|
121
|
+
* agent-editable scope docs render differently at boot (see renderGrouped). */
|
|
122
|
+
plugin?: string;
|
|
114
123
|
/** Absolute path to the source .md. */
|
|
115
124
|
path: string;
|
|
116
125
|
/** Document body, frontmatter stripped. */
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// never throw): invalid `surfaces` entries are dropped rather than crashing a
|
|
9
9
|
// render, and `crtr memory lint` owns strict authoring-time enforcement.
|
|
10
10
|
import { PROFILE_PROJECT_MEMORY_VALUES } from '../../api/dto/profiles.js';
|
|
11
|
+
import { surfaceEntryGatePasses } from './gate.js';
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
13
|
// Kinds — the two semantic kinds (design §3): `knowledge` (consult — procedural
|
|
13
14
|
// playbooks + factual references merged) vs `preference` (behave — standing
|
|
@@ -43,18 +44,18 @@ export function minRung(a, b) {
|
|
|
43
44
|
// ---------------------------------------------------------------------------
|
|
44
45
|
// Display-name normalization — the optional `NN-` ordering pin. A doc's
|
|
45
46
|
// physical path may carry a two-digit numeric prefix on a file or directory
|
|
46
|
-
// segment (`00-runtime-base
|
|
47
|
+
// segment (`00-runtime-base/`, `01-spine/`) purely to pin structural order
|
|
47
48
|
// against natural sort (design: builtin prompt corpus, used sparingly). The
|
|
48
49
|
// prefix is NEVER part of the doc's identity: every identity-facing surface —
|
|
49
50
|
// MemoryDoc.name, direct lookup, leaf fallback, `crtr memory read`/`list`,
|
|
50
51
|
// prompt render, on-read display — derives from the NORMALIZED segments, so
|
|
51
|
-
// `00-runtime-base` displays/dedups/resolves as `runtime-base`. When a doc
|
|
52
|
+
// `00-runtime-base/00-authoring` displays/dedups/resolves as `runtime-base/authoring`. When a doc
|
|
52
53
|
// sets an explicit frontmatter `name`, that normalized name wins instead of
|
|
53
54
|
// the path-derived fallback. Only the physical path keeps the prefix.
|
|
54
55
|
// ---------------------------------------------------------------------------
|
|
55
56
|
const NUMERIC_PREFIX_RE = /^\d{2}-/;
|
|
56
57
|
/** Strip an optional `NN-` ordering prefix from ONE path segment (file or
|
|
57
|
-
* directory display name). `00-runtime-base` -> `runtime-base`; `spine` (no
|
|
58
|
+
* directory display name). `00-runtime-base/00-authoring` -> `runtime-base/authoring`; `spine` (no
|
|
58
59
|
* prefix) is unchanged. */
|
|
59
60
|
export function normalizeNameSegment(segment) {
|
|
60
61
|
return segment.replace(NUMERIC_PREFIX_RE, '');
|
|
@@ -95,12 +96,13 @@ export const SURFACE_EVENTS = ['boot', 'workspace-open', 'read', 'memory-read',
|
|
|
95
96
|
* absence of an entry. (`Rung`'s `none` survives only as the internal
|
|
96
97
|
* ranking floor, e.g. `bootRung` of a doc with no boot entry.) */
|
|
97
98
|
export const SURFACE_RUNGS = ['name', 'preview', 'content'];
|
|
98
|
-
/** A doc's boot rung
|
|
99
|
-
* it carries
|
|
100
|
-
|
|
99
|
+
/** A doc's boot rung for `subject`: the highest `at` over participating
|
|
100
|
+
* `boot` entries, `none` when it carries none. The boot render's per-doc
|
|
101
|
+
* selection input. */
|
|
102
|
+
export function bootRung(doc, subject) {
|
|
101
103
|
let r = 'none';
|
|
102
104
|
for (const e of doc.surfaces) {
|
|
103
|
-
if (e.on !== 'boot')
|
|
105
|
+
if (e.on !== 'boot' || !surfaceEntryGatePasses(e, subject))
|
|
104
106
|
continue;
|
|
105
107
|
if (rungRank(e.at) > rungRank(r))
|
|
106
108
|
r = e.at;
|
|
@@ -142,7 +144,7 @@ export function parseSubstrateDoc(doc) {
|
|
|
142
144
|
const schema = parseSubstrateFrontmatter(doc.frontmatter);
|
|
143
145
|
if (schema === null)
|
|
144
146
|
return null;
|
|
145
|
-
return { ...schema, name: doc.name, scope: doc.scope, path: doc.path, body: doc.body, projectMemory: doc.projectMemory };
|
|
147
|
+
return { ...schema, name: doc.name, scope: doc.scope, plugin: doc.plugin, path: doc.path, body: doc.body, projectMemory: doc.projectMemory };
|
|
146
148
|
}
|
|
147
149
|
/** The kind a doc resolves to for `--kind` filtering/matching: the substrate
|
|
148
150
|
* kind when the frontmatter parses as a valid substrate doc, else the raw
|
|
@@ -194,8 +196,8 @@ function parseGate(v) {
|
|
|
194
196
|
* dropped (bad `on`, bad `at`, a read/memory-read/command entry left with
|
|
195
197
|
* neither `match` nor — read only — `match-frontmatter`). `match-frontmatter`
|
|
196
198
|
* on a non-read event is dropped from the entry, which survives iff it still
|
|
197
|
-
* carries a `match
|
|
198
|
-
* over many docs and must never throw. */
|
|
199
|
+
* carries a `match`; an invalid entry `gate` is dropped. Lint owns strict
|
|
200
|
+
* enforcement; the runtime parser maps over many docs and must never throw. */
|
|
199
201
|
function parseSurfaces(v) {
|
|
200
202
|
if (!Array.isArray(v))
|
|
201
203
|
return [];
|
|
@@ -212,6 +214,7 @@ function parseSurfaces(v) {
|
|
|
212
214
|
continue;
|
|
213
215
|
const match = parseMatchGlobs(rec['match']);
|
|
214
216
|
const matchFrontmatter = on === 'read' ? parseGate(rec['match-frontmatter']) : undefined;
|
|
217
|
+
const gate = parseGate(rec['gate']);
|
|
215
218
|
if ((on === 'read' || on === 'memory-read' || on === 'command') &&
|
|
216
219
|
match === undefined &&
|
|
217
220
|
matchFrontmatter === undefined) {
|
|
@@ -222,6 +225,7 @@ function parseSurfaces(v) {
|
|
|
222
225
|
at: at,
|
|
223
226
|
...(match !== undefined ? { match } : {}),
|
|
224
227
|
...(matchFrontmatter !== undefined ? { matchFrontmatter } : {}),
|
|
228
|
+
...(gate !== undefined ? { gate } : {}),
|
|
225
229
|
});
|
|
226
230
|
}
|
|
227
231
|
return out;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Rung, type SubstrateDoc, type SurfaceEntry } from './schema.js';
|
|
2
|
+
import type { NodeConfigSubject } from './subject-fields.js';
|
|
2
3
|
/** The project directory owning a doc's store: the path segment stack above
|
|
3
4
|
* the doc's `.crouter` dir. `null` when the doc's path carries no `.crouter`
|
|
4
5
|
* segment (builtin docs). */
|
|
@@ -13,20 +14,20 @@ export declare function underOwningRoot(doc: Pick<SubstrateDoc, 'path'>, absFile
|
|
|
13
14
|
* realpathed. `readFrontmatter` is the read file's own parsed YAML (empty
|
|
14
15
|
* for non-markdown files); an empty record never satisfies
|
|
15
16
|
* `matchFrontmatter`. */
|
|
16
|
-
export declare function matchesReadEntry(entry: SurfaceEntry, doc: Pick<SubstrateDoc, 'path' | 'scope'>, absReadFile: string, readFrontmatter: Record<string, unknown>): boolean;
|
|
17
|
+
export declare function matchesReadEntry(entry: SurfaceEntry, doc: Pick<SubstrateDoc, 'path' | 'scope'>, subject: NodeConfigSubject, absReadFile: string, readFrontmatter: Record<string, unknown>): boolean;
|
|
17
18
|
/** Does a `memory-read` entry on the doc named `carrierName` fit a resolved
|
|
18
19
|
* read of a doc known by `targetNames`? */
|
|
19
|
-
export declare function matchesMemoryReadEntry(entry: SurfaceEntry, carrierName: string, targetNames: string[]): boolean;
|
|
20
|
+
export declare function matchesMemoryReadEntry(entry: SurfaceEntry, carrierName: string, subject: NodeConfigSubject | null, targetNames: string[]): boolean;
|
|
20
21
|
/** Does a `command` entry fit the executed command string? */
|
|
21
|
-
export declare function matchesCommandEntry(entry: SurfaceEntry, command: string): boolean;
|
|
22
|
+
export declare function matchesCommandEntry(entry: SurfaceEntry, subject: NodeConfigSubject, command: string): boolean;
|
|
22
23
|
/** The doc's delivery rung for a read of `absReadFile` (already realpathed). */
|
|
23
|
-
export declare function readDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces' | 'path' | 'scope'>, absReadFile: string, readFrontmatter: Record<string, unknown>): Rung;
|
|
24
|
+
export declare function readDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces' | 'path' | 'scope'>, subject: NodeConfigSubject, absReadFile: string, readFrontmatter: Record<string, unknown>): Rung;
|
|
24
25
|
/** The doc's workspace-open rung. Presence of an entry is the match; callers
|
|
25
26
|
* scope the corpus to project stores mounted by the subject's cwd/profile. */
|
|
26
|
-
export declare function workspaceOpenRung(doc: Pick<SubstrateDoc, 'surfaces'
|
|
27
|
+
export declare function workspaceOpenRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject): Rung;
|
|
27
28
|
/** The doc's delivery rung for a `crtr memory read` that resolved a doc
|
|
28
29
|
* known by `targetNames` (canonical name, plus the mount-stripped name for
|
|
29
30
|
* plugin-mounted docs). The doc's own `name` anchors `./` globs. */
|
|
30
|
-
export declare function memoryReadDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces' | 'name'>, targetNames: string[]): Rung;
|
|
31
|
+
export declare function memoryReadDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces' | 'name'>, subject: NodeConfigSubject | null, targetNames: string[]): Rung;
|
|
31
32
|
/** The doc's delivery rung for an executed command string. */
|
|
32
|
-
export declare function commandDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces'>, command: string): Rung;
|
|
33
|
+
export declare function commandDeliveryRung(doc: Pick<SubstrateDoc, 'surfaces'>, subject: NodeConfigSubject, command: string): Rung;
|
|
@@ -13,6 +13,7 @@ import { basename, isAbsolute, matchesGlob, relative, sep } from 'node:path';
|
|
|
13
13
|
import { CRTR_DIR_NAME } from '../../types.js';
|
|
14
14
|
import { realpathOrSelf } from '../fs-utils.js';
|
|
15
15
|
import { evalCondition } from '../predicate.js';
|
|
16
|
+
import { surfaceEntryGatePasses } from './gate.js';
|
|
16
17
|
import { rungRank } from './schema.js';
|
|
17
18
|
/** The project directory owning a doc's store: the path segment stack above
|
|
18
19
|
* the doc's `.crouter` dir. `null` when the doc's path carries no `.crouter`
|
|
@@ -62,8 +63,8 @@ function readGlobMatches(glob, doc, absReadFile) {
|
|
|
62
63
|
* realpathed. `readFrontmatter` is the read file's own parsed YAML (empty
|
|
63
64
|
* for non-markdown files); an empty record never satisfies
|
|
64
65
|
* `matchFrontmatter`. */
|
|
65
|
-
export function matchesReadEntry(entry, doc, absReadFile, readFrontmatter) {
|
|
66
|
-
if (entry.on !== 'read')
|
|
66
|
+
export function matchesReadEntry(entry, doc, subject, absReadFile, readFrontmatter) {
|
|
67
|
+
if (entry.on !== 'read' || !surfaceEntryGatePasses(entry, subject))
|
|
67
68
|
return false;
|
|
68
69
|
if (entry.match !== undefined && !entry.match.some((g) => readGlobMatches(g, doc, absReadFile))) {
|
|
69
70
|
return false;
|
|
@@ -97,8 +98,8 @@ function memoryReadGlobMatches(glob, carrierName, targetNames) {
|
|
|
97
98
|
}
|
|
98
99
|
/** Does a `memory-read` entry on the doc named `carrierName` fit a resolved
|
|
99
100
|
* read of a doc known by `targetNames`? */
|
|
100
|
-
export function matchesMemoryReadEntry(entry, carrierName, targetNames) {
|
|
101
|
-
if (entry.on !== 'memory-read' || entry.match === undefined)
|
|
101
|
+
export function matchesMemoryReadEntry(entry, carrierName, subject, targetNames) {
|
|
102
|
+
if (entry.on !== 'memory-read' || entry.match === undefined || !surfaceEntryGatePasses(entry, subject))
|
|
102
103
|
return false;
|
|
103
104
|
return entry.match.some((g) => memoryReadGlobMatches(g, carrierName, targetNames));
|
|
104
105
|
}
|
|
@@ -119,8 +120,8 @@ function commandGlobMatches(glob, command) {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
/** Does a `command` entry fit the executed command string? */
|
|
122
|
-
export function matchesCommandEntry(entry, command) {
|
|
123
|
-
if (entry.on !== 'command' || entry.match === undefined)
|
|
123
|
+
export function matchesCommandEntry(entry, subject, command) {
|
|
124
|
+
if (entry.on !== 'command' || entry.match === undefined || !surfaceEntryGatePasses(entry, subject))
|
|
124
125
|
return false;
|
|
125
126
|
return entry.match.some((g) => commandGlobMatches(g, command));
|
|
126
127
|
}
|
|
@@ -131,10 +132,10 @@ export function matchesCommandEntry(entry, command) {
|
|
|
131
132
|
// floor, never an authored rung).
|
|
132
133
|
// ---------------------------------------------------------------------------
|
|
133
134
|
/** The doc's delivery rung for a read of `absReadFile` (already realpathed). */
|
|
134
|
-
export function readDeliveryRung(doc, absReadFile, readFrontmatter) {
|
|
135
|
+
export function readDeliveryRung(doc, subject, absReadFile, readFrontmatter) {
|
|
135
136
|
let r = 'none';
|
|
136
137
|
for (const e of doc.surfaces) {
|
|
137
|
-
if (!matchesReadEntry(e, doc, absReadFile, readFrontmatter))
|
|
138
|
+
if (!matchesReadEntry(e, doc, subject, absReadFile, readFrontmatter))
|
|
138
139
|
continue;
|
|
139
140
|
if (rungRank(e.at) > rungRank(r))
|
|
140
141
|
r = e.at;
|
|
@@ -143,10 +144,10 @@ export function readDeliveryRung(doc, absReadFile, readFrontmatter) {
|
|
|
143
144
|
}
|
|
144
145
|
/** The doc's workspace-open rung. Presence of an entry is the match; callers
|
|
145
146
|
* scope the corpus to project stores mounted by the subject's cwd/profile. */
|
|
146
|
-
export function workspaceOpenRung(doc) {
|
|
147
|
+
export function workspaceOpenRung(doc, subject) {
|
|
147
148
|
let r = 'none';
|
|
148
149
|
for (const e of doc.surfaces) {
|
|
149
|
-
if (e.on !== 'workspace-open')
|
|
150
|
+
if (e.on !== 'workspace-open' || !surfaceEntryGatePasses(e, subject))
|
|
150
151
|
continue;
|
|
151
152
|
if (rungRank(e.at) > rungRank(r))
|
|
152
153
|
r = e.at;
|
|
@@ -156,10 +157,10 @@ export function workspaceOpenRung(doc) {
|
|
|
156
157
|
/** The doc's delivery rung for a `crtr memory read` that resolved a doc
|
|
157
158
|
* known by `targetNames` (canonical name, plus the mount-stripped name for
|
|
158
159
|
* plugin-mounted docs). The doc's own `name` anchors `./` globs. */
|
|
159
|
-
export function memoryReadDeliveryRung(doc, targetNames) {
|
|
160
|
+
export function memoryReadDeliveryRung(doc, subject, targetNames) {
|
|
160
161
|
let r = 'none';
|
|
161
162
|
for (const e of doc.surfaces) {
|
|
162
|
-
if (!matchesMemoryReadEntry(e, doc.name, targetNames))
|
|
163
|
+
if (!matchesMemoryReadEntry(e, doc.name, subject, targetNames))
|
|
163
164
|
continue;
|
|
164
165
|
if (rungRank(e.at) > rungRank(r))
|
|
165
166
|
r = e.at;
|
|
@@ -167,10 +168,10 @@ export function memoryReadDeliveryRung(doc, targetNames) {
|
|
|
167
168
|
return r;
|
|
168
169
|
}
|
|
169
170
|
/** The doc's delivery rung for an executed command string. */
|
|
170
|
-
export function commandDeliveryRung(doc, command) {
|
|
171
|
+
export function commandDeliveryRung(doc, subject, command) {
|
|
171
172
|
let r = 'none';
|
|
172
173
|
for (const e of doc.surfaces) {
|
|
173
|
-
if (!matchesCommandEntry(e, command))
|
|
174
|
+
if (!matchesCommandEntry(e, subject, command))
|
|
174
175
|
continue;
|
|
175
176
|
if (rungRank(e.at) > rungRank(r))
|
|
176
177
|
r = e.at;
|
|
@@ -326,6 +326,10 @@ export declare const CONFIG_SETTINGS_COVERAGE: {
|
|
|
326
326
|
surface: "specialized";
|
|
327
327
|
owner: string;
|
|
328
328
|
};
|
|
329
|
+
humanActions: {
|
|
330
|
+
surface: "specialized";
|
|
331
|
+
owner: string;
|
|
332
|
+
};
|
|
329
333
|
};
|
|
330
334
|
/** The exhaustive top-level key list used by config persistence and help. */
|
|
331
335
|
export declare const SCOPE_CONFIG_KEYS: (keyof ScopeConfig)[];
|
|
@@ -389,6 +389,7 @@ export const CONFIG_SETTINGS_COVERAGE = {
|
|
|
389
389
|
remoteCanvas: { surface: 'specialized', owner: 'canvas config commands' },
|
|
390
390
|
spawnEnv: { surface: 'specialized', owner: 'spawn environment allow-list' },
|
|
391
391
|
bin: { surface: 'specialized', owner: 'bare-binary contributions on every node PATH' },
|
|
392
|
+
humanActions: { surface: 'specialized', owner: 'scope-configured human completion actions' },
|
|
392
393
|
};
|
|
393
394
|
/** The exhaustive top-level key list used by config persistence and help. */
|
|
394
395
|
export const SCOPE_CONFIG_KEYS = Object.keys(CONFIG_SETTINGS_COVERAGE);
|