@north-light/crouter 0.3.226 → 0.3.228
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 +5 -5
- package/dist/api/client.js +3 -3
- package/dist/api/dto/broker-ops.d.ts +8 -0
- package/dist/api/dto/memory.d.ts +8 -7
- package/dist/api/dto/memory.js +2 -2
- package/dist/builtin-memory/05-kinds/design/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +4 -3
- package/dist/builtin-memory/05-kinds/design/design-contract.md +3 -6
- package/dist/builtin-memory/05-kinds/plan/00-base.md +3 -4
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +4 -5
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +5 -13
- package/dist/builtin-memory/design/guide.md +38 -16
- package/dist/builtin-memory/design/roadmap.md +4 -4
- package/dist/builtin-memory/insights/init.md +5 -5
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +6 -6
- package/dist/builtin-memory/internal/plugins.md +1 -1
- package/dist/builtin-memory/plan/guide.md +53 -0
- package/dist/builtin-memory/plan/roadmap.md +10 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +50 -37
- package/dist/clients/attach/__tests__/pane-tag-successor.test.js +56 -0
- package/dist/clients/attach/__tests__/profile-files.test.js +21 -0
- package/dist/clients/attach/chrome/inbox-strip.js +11 -7
- package/dist/clients/attach/session/pane-tag.d.ts +28 -2
- package/dist/clients/attach/session/pane-tag.js +48 -22
- package/dist/clients/attach/session/profile-files.d.ts +6 -0
- package/dist/clients/attach/session/profile-files.js +44 -11
- package/dist/clients/attach/slash/dispatch.js +4 -7
- package/dist/clients/attach/viewer.js +588 -596
- package/dist/commands/__tests__/surface-reload-target.test.js +21 -0
- package/dist/commands/api-client.d.ts +3 -7
- package/dist/commands/api-client.js +7 -12
- package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +292 -0
- package/dist/commands/memory/__tests__/repository-root-lint.test.js +146 -0
- package/dist/commands/memory/delete.js +51 -24
- package/dist/commands/memory/edit.js +50 -10
- package/dist/commands/memory/find.js +105 -56
- package/dist/commands/memory/history.js +51 -31
- package/dist/commands/memory/lint.d.ts +0 -9
- package/dist/commands/memory/lint.js +78 -322
- package/dist/commands/memory/list.d.ts +12 -4
- package/dist/commands/memory/list.js +64 -25
- package/dist/commands/memory/move.js +103 -73
- package/dist/commands/memory/origin.js +35 -5
- package/dist/commands/memory/read.d.ts +4 -0
- package/dist/commands/memory/read.js +132 -80
- package/dist/commands/memory/shared.d.ts +115 -25
- package/dist/commands/memory/shared.js +331 -74
- package/dist/commands/memory/write.js +72 -21
- package/dist/commands/memory.js +2 -2
- package/dist/commands/node/lifecycle.js +2 -2
- package/dist/commands/pkg/market-manage.js +25 -24
- package/dist/commands/pkg/plugin-manage.d.ts +22 -6
- package/dist/commands/pkg/plugin-manage.js +118 -25
- package/dist/commands/pkg/shared.d.ts +8 -0
- package/dist/commands/pkg/shared.js +23 -20
- package/dist/commands/surface-reload.d.ts +5 -0
- package/dist/commands/surface-reload.js +9 -1
- package/dist/commands/sys/__tests__/migrate.test.js +1140 -22
- package/dist/commands/sys/__tests__/sync-project-guidance.test.js +218 -0
- package/dist/commands/sys/migrate.js +126 -140
- package/dist/commands/sys/panels/profiles-panel.d.ts +66 -0
- package/dist/commands/sys/panels/profiles-panel.js +599 -0
- package/dist/commands/sys/settings-shell.d.ts +4 -2
- package/dist/commands/sys/settings-shell.js +35 -4
- package/dist/commands/sys/settings.js +21 -5
- package/dist/commands/sys/sync-deps.d.ts +2 -0
- package/dist/commands/sys/sync-deps.js +26 -15
- package/dist/commands/sys/sync-project-guidance.js +228 -145
- package/dist/commands/sys/sync-skills.js +28 -17
- package/dist/commands/sys/update.js +11 -3
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +15 -2
- package/dist/core/__tests__/config-change-delta.test.js +106 -0
- package/dist/core/__tests__/context-intro.test.js +13 -9
- package/dist/core/__tests__/daemon-boot.test.js +22 -16
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +5 -1
- package/dist/core/__tests__/human-deliver.test.js +19 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +50 -46
- package/dist/core/__tests__/{serial → integration}/broker-fork-seam.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-sdk-wiring.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/broker-snapshot-history.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/command-plugins.test.js +94 -3
- package/dist/core/__tests__/{serial → integration}/deferred-no-wake.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/flagship-lifecycle.test.js +13 -27
- package/dist/core/__tests__/{serial → integration}/host-teardown-process-group.test.js +1 -1
- package/dist/core/__tests__/{serial → integration}/human-deliver-e2e.test.js +1 -1
- package/dist/core/__tests__/{live-mutation-verbs.test.js → integration/live-mutation-verbs.test.js} +22 -33
- package/dist/core/__tests__/{serial → integration}/live-mutation.test.js +21 -41
- package/dist/core/__tests__/integration/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/refresh-stall-recycle.test.js +1 -1
- package/dist/core/__tests__/integration/revive.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/revive.test.js +38 -19
- package/dist/core/__tests__/integration/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/spawn-root.test.js +104 -1
- package/dist/core/__tests__/integration/subscription-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/subscription-delivery.test.js +1 -1
- package/dist/core/__tests__/integration/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/{serial → integration}/tmux-surface.test.js +1 -1
- package/dist/core/__tests__/integration/worktree-land.test.d.ts +1 -0
- package/dist/core/__tests__/integration/worktree-land.test.js +400 -0
- package/dist/core/__tests__/integration/worktree-reap.test.d.ts +1 -0
- package/dist/core/__tests__/{serial/worktree.test.js → integration/worktree-reap.test.js} +6 -338
- package/dist/core/__tests__/kickoff.test.js +16 -5
- package/dist/core/__tests__/memory-resolver-precedence.test.js +122 -91
- package/dist/core/__tests__/nested-store-discovery.test.js +5 -3
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +2 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +39 -27
- package/dist/core/__tests__/on-read-nested-store.test.js +19 -13
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +138 -44
- package/dist/core/__tests__/repository-association.test.d.ts +1 -0
- package/dist/core/__tests__/repository-association.test.js +153 -0
- package/dist/core/__tests__/repository-root-identity.test.d.ts +1 -0
- package/dist/core/__tests__/repository-root-identity.test.js +219 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +27 -11
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.d.ts +1 -0
- package/dist/core/__tests__/warm-claim-preference-snapshot.test.js +40 -0
- package/dist/core/canvas/canvas.d.ts +3 -0
- package/dist/core/canvas/canvas.js +30 -10
- package/dist/core/canvas/db.js +28 -1
- package/dist/core/canvas/paths.d.ts +4 -6
- package/dist/core/canvas/paths.js +8 -6
- package/dist/core/canvas/render-source.js +2 -2
- package/dist/core/canvas/types.d.ts +9 -13
- package/dist/core/exclusive-lock.d.ts +2 -0
- package/dist/core/exclusive-lock.js +21 -0
- package/dist/core/git.d.ts +0 -1
- package/dist/core/git.js +0 -3
- package/dist/core/human/__tests__/integration/inbox-core.test.d.ts +1 -0
- package/dist/core/human/feedback-companion.js +3 -0
- package/dist/core/human/scan.js +8 -1
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +2 -1
- package/dist/core/memory/doc-link-grammar.js +2 -2
- package/dist/core/memory/history.d.ts +24 -0
- package/dist/core/memory/history.js +66 -1
- package/dist/core/memory/identity.d.ts +65 -0
- package/dist/core/memory/identity.js +185 -0
- package/dist/core/memory/inline-ref-guidance.d.ts +1 -1
- package/dist/core/memory/inline-ref-guidance.js +1 -1
- package/dist/core/memory/inline-ref-inventory.d.ts +3 -10
- package/dist/core/memory/inline-ref-inventory.js +30 -62
- package/dist/core/memory/lint.d.ts +129 -0
- package/dist/core/memory/lint.js +517 -0
- package/dist/core/memory/project-namespace.d.ts +31 -0
- package/dist/core/memory/project-namespace.js +85 -0
- package/dist/core/memory/repository-association.d.ts +31 -0
- package/dist/core/memory/repository-association.js +129 -0
- package/dist/core/memory/tree.d.ts +39 -0
- package/dist/core/memory/tree.js +93 -0
- package/dist/core/memory-resolver.d.ts +193 -90
- package/dist/core/memory-resolver.js +461 -375
- package/dist/core/nested-stores.js +6 -13
- package/dist/core/profiles/select.d.ts +4 -1
- package/dist/core/profiles/select.js +108 -48
- package/dist/core/review/__tests__/capture-origin.test.js +2 -2
- package/dist/core/review/__tests__/stage-identity.test.js +2 -2
- package/dist/core/review/companion.js +11 -2
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +3 -3
- package/dist/core/runtime/broker/daemon-ops.d.ts +2 -2
- package/dist/core/runtime/broker/rebind.js +5 -0
- package/dist/core/runtime/broker-extension-render.d.ts +7 -3
- package/dist/core/runtime/broker-extension-render.js +10 -5
- package/dist/core/runtime/broker-persona-guidance.d.ts +19 -5
- package/dist/core/runtime/broker-persona-guidance.js +122 -27
- package/dist/core/runtime/deliver-live.d.ts +16 -4
- package/dist/core/runtime/deliver-live.js +29 -15
- package/dist/core/runtime/kickoff.d.ts +3 -3
- package/dist/core/runtime/kickoff.js +8 -16
- package/dist/core/runtime/lifecycle.js +2 -3
- package/dist/core/runtime/nodes.d.ts +3 -4
- package/dist/core/runtime/nodes.js +3 -4
- package/dist/core/runtime/persona.d.ts +8 -12
- package/dist/core/runtime/persona.js +24 -96
- package/dist/core/runtime/promote.d.ts +2 -2
- package/dist/core/runtime/promote.js +8 -21
- package/dist/core/runtime/revive.js +20 -17
- package/dist/core/runtime/spawn.js +18 -7
- package/dist/core/runtime/tmux-bindings.js +2 -3
- package/dist/core/runtime/warm-pool.js +3 -4
- package/dist/core/scope.js +2 -0
- package/dist/core/self-update.d.ts +0 -2
- package/dist/core/self-update.js +2 -35
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.d.ts +1 -0
- package/dist/core/substrate/__tests__/surface-match-memory-read.test.js +28 -0
- package/dist/core/substrate/index.d.ts +2 -2
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.d.ts +43 -27
- package/dist/core/substrate/injected-store.js +208 -104
- package/dist/core/substrate/listings.d.ts +19 -12
- package/dist/core/substrate/listings.js +75 -52
- package/dist/core/substrate/on-read-node.d.ts +4 -7
- package/dist/core/substrate/on-read-node.js +6 -8
- package/dist/core/substrate/on-read.d.ts +22 -25
- package/dist/core/substrate/on-read.js +103 -147
- package/dist/core/substrate/render-node.d.ts +4 -7
- package/dist/core/substrate/render-node.js +5 -7
- package/dist/core/substrate/render.d.ts +21 -3
- package/dist/core/substrate/render.js +291 -223
- package/dist/core/substrate/schema.d.ts +1 -13
- package/dist/core/substrate/schema.js +5 -40
- package/dist/core/substrate/session-cache.d.ts +14 -4
- package/dist/core/substrate/session-cache.js +40 -22
- package/dist/core/substrate/surface-match.d.ts +9 -7
- package/dist/core/substrate/surface-match.js +26 -25
- package/dist/daemon/__tests__/helpers/source-daemon.d.ts +30 -0
- package/dist/daemon/__tests__/helpers/source-daemon.js +174 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.d.ts +1 -0
- package/dist/daemon/__tests__/integration/migration-startup.test.js +97 -0
- package/dist/daemon/api/__tests__/bridge-heartbeat.test.js +37 -9
- package/dist/daemon/api/bridge.d.ts +35 -4
- package/dist/daemon/api/bridge.js +50 -8
- package/dist/daemon/api/handlers/broker-ops.js +21 -16
- package/dist/daemon/api/handlers/memory.js +2 -0
- package/dist/daemon/crtrd.js +2 -0
- package/dist/daemon/human/finish.js +8 -1
- package/dist/daemon/manage.d.ts +0 -1
- package/dist/daemon/manage.js +7 -16
- package/dist/daemon/startup-policy.d.ts +1 -0
- package/dist/daemon/startup-policy.js +1 -0
- package/dist/migrations/001-surfaces-frontmatter.js +21 -109
- package/dist/migrations/002-profile-project-memory.js +1 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +26 -0
- package/dist/migrations/003-repository-root-memory-identity/front-door.js +231 -0
- package/dist/migrations/003-repository-root-memory-identity/index.d.ts +2 -0
- package/dist/migrations/003-repository-root-memory-identity/index.js +513 -0
- package/dist/migrations/003-repository-root-memory-identity/references.d.ts +95 -0
- package/dist/migrations/003-repository-root-memory-identity/references.js +469 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +39 -0
- package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +349 -0
- package/dist/migrations/__tests__/activation-concurrency.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation-concurrency.test.js +145 -0
- package/dist/migrations/__tests__/activation.test.d.ts +1 -0
- package/dist/migrations/__tests__/activation.test.js +149 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.d.ts +1 -0
- package/dist/migrations/__tests__/deletion-and-root-declaration.test.js +149 -0
- package/dist/migrations/activation.d.ts +16 -0
- package/dist/migrations/activation.js +78 -0
- package/dist/migrations/convergent.d.ts +14 -3
- package/dist/migrations/convergent.js +21 -10
- package/dist/migrations/corpus.d.ts +78 -0
- package/dist/migrations/corpus.js +497 -0
- package/dist/migrations/frontmatter-splice.d.ts +15 -0
- package/dist/migrations/frontmatter-splice.js +176 -0
- package/dist/migrations/registry.d.ts +6 -1
- package/dist/migrations/registry.js +7 -2
- package/dist/migrations/runner.d.ts +41 -0
- package/dist/migrations/runner.js +81 -0
- package/dist/migrations/types.d.ts +148 -9
- package/dist/migrations/types.js +9 -2
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +225 -17
- package/dist/pi-extensions/__tests__/canvas-goal-capture-envelope.test.js +11 -3
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -5
- package/dist/pi-extensions/canvas-context-intro.js +50 -46
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +1 -8
- package/dist/pi-extensions/canvas-doc-substrate.js +55 -122
- package/dist/pi-extensions/canvas-stophook.js +6 -13
- package/dist/shared/generated-context.d.ts +0 -3
- package/dist/shared/generated-context.js +0 -57
- package/dist/shared/tool-groups.js +2 -3
- package/package.json +5 -4
- package/runtime.lock.json +2 -2
- /package/dist/api/__tests__/{serial → integration}/client.test.d.ts +0 -0
- /package/dist/api/__tests__/{serial → integration}/client.test.js +0 -0
- /package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{serial → integration}/provider-rotation.test.ts +0 -0
- /package/dist/clients/{inbox/__tests__/serial/inbox-controller.test.d.ts → attach/__tests__/pane-tag-successor.test.d.ts} +0 -0
- /package/dist/clients/{inbox/__tests__/serial/mount-panel.test.d.ts → attach/__tests__/profile-files.test.d.ts} +0 -0
- /package/dist/{core/__tests__/live-mutation-verbs.test.d.ts → clients/inbox/__tests__/integration/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/inbox-controller.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-fork-seam.test.d.ts → clients/inbox/__tests__/integration/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{serial → integration}/mount-panel.test.js +0 -0
- /package/dist/{core/__tests__/serial/broker-sdk-wiring.test.d.ts → commands/__tests__/surface-reload-target.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/broker-snapshot-history.test.d.ts → commands/memory/__tests__/command-selector-and-mutation-guards.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/command-plugins.test.d.ts → commands/memory/__tests__/repository-root-lint.test.d.ts} +0 -0
- /package/dist/{core/__tests__/serial/deferred-no-wake.test.d.ts → commands/sys/__tests__/sync-project-guidance.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/flagship-lifecycle.test.d.ts → config-change-delta.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/host-teardown-process-group.test.d.ts → integration/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/human-deliver-e2e.test.d.ts → integration/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/live-mutation.test.d.ts → integration/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/refresh-stall-recycle.test.d.ts → integration/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/revive.test.d.ts → integration/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/spawn-root.test.d.ts → integration/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/subscription-delivery.test.d.ts → integration/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/tmux-surface.test.d.ts → integration/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{serial/worktree.test.d.ts → integration/live-mutation-verbs.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/serial/inbox-core.test.d.ts → __tests__/integration/live-mutation.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{serial → integration}/inbox-core.test.js +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// render.ts — the pure
|
|
1
|
+
// render.ts — the pure render functions for the document substrate: two boot
|
|
2
|
+
// targets plus the config-change delta.
|
|
2
3
|
//
|
|
3
4
|
// Two boot targets, one shape per kind: each kind renders as ONE wrapped block —
|
|
4
5
|
// intro prose → GROUPED content → an update directive. The render is grouped
|
|
@@ -6,12 +7,13 @@
|
|
|
6
7
|
// clean prose bodies (verbatim, no tree chrome, no name label — this is what
|
|
7
8
|
// makes content-rung the persona-prose mechanism), in general→specific order;
|
|
8
9
|
// then the `preview`+`name` docs render as ONE catalog tree, each at its own
|
|
9
|
-
// rung (preview → a `# read when:` routing line; name → a bare entry)
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
10
|
+
// rung (preview → a `# read when:` routing line; name → a bare entry), a
|
|
11
|
+
// directory that carries its own document rendering that entry on the
|
|
12
|
+
// directory's own row; a doc with NO boot entry leaks only into its dir's
|
|
13
|
+
// `[+N more]` count, never a name — EXCEPT a node-store doc with no boot entry:
|
|
14
|
+
// renderKnowledgeForSubject floors it up to `name` so it still shows its bare
|
|
15
|
+
// name rather than collapsing into a hidden count. A node-store doc with boot
|
|
16
|
+
// entries remains absent when none of those entries participates.
|
|
15
17
|
//
|
|
16
18
|
// • the SYSTEM-PROMPT half — `<memory kind="preference">`
|
|
17
19
|
// (renderPreferencesSection) — the always-present memory-usage guidance plus
|
|
@@ -20,51 +22,59 @@
|
|
|
20
22
|
// `\n\nGuidelines:` anchor;
|
|
21
23
|
// • the first-message half — `<memory kind="knowledge">`
|
|
22
24
|
// (renderKnowledgeBlock) — the consultable catalog (every `kind: knowledge`
|
|
23
|
-
// doc ∪ node
|
|
25
|
+
// doc ∪ the node's own store) the string bearings.ts embeds in the session_start
|
|
24
26
|
// message as a SIBLING of `<crtr-bearings>` (never nested under the context
|
|
25
27
|
// dir — the catalog spans all scopes and belongs to no one directory).
|
|
26
28
|
//
|
|
29
|
+
// renderConfigChangeDelta runs the same winner assemblies after a configuration
|
|
30
|
+
// change and appends only identities newly visible or raised above the active
|
|
31
|
+
// context rank. It never rewrites the frozen system snapshot.
|
|
32
|
+
//
|
|
27
33
|
// The pipeline is explicitly TWO-STEP and must never run in the other order:
|
|
28
34
|
// 1. SELECT WINNERS — MemoryDoc → parseSubstrateDoc → (null-filter
|
|
29
35
|
// non-substrate) → per-doc boot rung (`bootRung`, the max `at` over
|
|
30
36
|
// participating `on: boot` surfaces entries) → gatePasses → first-wins DEDUP by
|
|
31
37
|
// name over the resolver's precedence order (nearest project > user >
|
|
32
|
-
// builtin). Node-
|
|
33
|
-
// knowledge-block render floors only a node-
|
|
34
|
-
// `name` — the one rung-floor exemption, scoped to the node
|
|
35
|
-
// only.
|
|
38
|
+
// builtin). Node-store docs follow the same gate rule, but the
|
|
39
|
+
// knowledge-block render floors only a node-store doc with no boot entry to
|
|
40
|
+
// `name` — the one rung-floor exemption, scoped to the node store only.
|
|
36
41
|
// 2. RENDER IN DISPLAY ORDER — split winners into content vs. the rest;
|
|
37
42
|
// order content general→specific (builtin, user, project outermost→
|
|
38
|
-
// nearest, node
|
|
43
|
+
// nearest, node store last) then by tree position then filename; place
|
|
39
44
|
// the rest into the catalog tree (position-ordered, as before).
|
|
40
45
|
// Sorting into display order BEFORE step 1's dedup would let a
|
|
41
46
|
// later-in-display-order builtin doc's array position mask a project/user
|
|
42
47
|
// override — dedup precedence and display order are deliberately different
|
|
43
48
|
// orderings and must not be conflated.
|
|
44
49
|
//
|
|
45
|
-
// Pure + defensive: reads the resolver
|
|
46
|
-
//
|
|
47
|
-
// caller-supplied
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
50
|
+
// Pure + defensive: reads the resolver (the node's own store included) and
|
|
51
|
+
// canvas-db (subject assembly); performs no writes and no side effects beyond
|
|
52
|
+
// the caller-supplied exposure target. Baseline renders register their snapshot
|
|
53
|
+
// source; transition renders query the active maximum and register transcript
|
|
54
|
+
// deliveries. A single malformed doc never throws the whole render — the
|
|
55
|
+
// resolver's loader isolates an unparseable file and parseSubstrateDoc returns
|
|
56
|
+
// null for a non-substrate doc.
|
|
57
|
+
//
|
|
58
|
+
// STRUCTURE comes from the canonical directory tree (core/memory/tree.ts) and
|
|
59
|
+
// ORDER from each descriptor's stored physical path, never from the canonical
|
|
60
|
+
// name's segments: a project store's namespace has no physical counterpart and
|
|
61
|
+
// an explicit `name:` detaches identity from the path. A directory that carries
|
|
62
|
+
// its own document is ONE row — the document renders at its boot rung on the
|
|
63
|
+
// directory's own line, with the directory's members nested beneath it.
|
|
64
|
+
import { sep } from 'node:path';
|
|
53
65
|
import { interpolateNodePaths } from '../canvas/paths.js';
|
|
54
|
-
import { listAllMemoryDocs, resolveMemoryDoc } from '../memory-resolver.js';
|
|
66
|
+
import { listAllMemoryDocs, loadMemoryTargetView, resolveMemoryDoc } from '../memory-resolver.js';
|
|
67
|
+
import { buildMemoryTree } from '../memory/tree.js';
|
|
55
68
|
import { subKindsAvailableTo } from '../config.js';
|
|
56
|
-
import {
|
|
57
|
-
import { pathExists, readText, realpathOrSelf, walkFiles } from '../fs-utils.js';
|
|
58
|
-
import { memoryDir } from '../runtime/memory.js';
|
|
59
|
-
import { NEUTRAL_PROJECT_MEMORY, projectScopeRoots } from '../scope.js';
|
|
69
|
+
import { projectScopeRoots } from '../scope.js';
|
|
60
70
|
import { gatePasses } from './gate.js';
|
|
61
71
|
// De-barreled to LEAF modules on purpose: importing from './index.js' would
|
|
62
72
|
// pull subject.js (canvas-db) transitively, re-tainting every CLI consumer of
|
|
63
73
|
// the pure render fns. The canvas-db wrappers (renderPreferencesSection /
|
|
64
74
|
// renderKnowledgeBlock) live in render-node.js instead.
|
|
65
|
-
import {
|
|
66
|
-
import { bootRung, minRung,
|
|
67
|
-
import { cachedSubstrateDocs } from './session-cache.js';
|
|
75
|
+
import { documentExposedAtOrAbove, registerDocumentExposure, } from './injected-store.js';
|
|
76
|
+
import { bootRung, minRung, parseSubstrateDoc, previewLine, rungRank, } from './schema.js';
|
|
77
|
+
import { cachedAllMemoryDocs, cachedSubstrateDocs } from './session-cache.js';
|
|
68
78
|
/** First-wins dedup by (already-normalized) name, preserving the input's
|
|
69
79
|
* precedence order. The array's ORDER is the precedence signal — callers must
|
|
70
80
|
* pass docs already ordered highest-precedence-first; this function never
|
|
@@ -96,34 +106,54 @@ function dedupeFirstWins(docs) {
|
|
|
96
106
|
* surviving doc's authored boot rung is clamped to its cap before dedup, the
|
|
97
107
|
* session cache, and delivery seeding see it. */
|
|
98
108
|
function selectWinners(subject, kind) {
|
|
99
|
-
let
|
|
109
|
+
let corpus;
|
|
100
110
|
try {
|
|
101
|
-
|
|
111
|
+
corpus = bootCorpus();
|
|
102
112
|
}
|
|
103
113
|
catch {
|
|
104
114
|
return [];
|
|
105
115
|
}
|
|
106
|
-
const eligible =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
const eligible = [];
|
|
117
|
+
for (const d of corpus.docs) {
|
|
118
|
+
if (d.kind !== kind || d.projectMemory === 'none' || !gatePasses(d, subject))
|
|
119
|
+
continue;
|
|
120
|
+
const source = corpus.sources.get(d.path);
|
|
121
|
+
if (source === undefined)
|
|
122
|
+
continue;
|
|
123
|
+
eligible.push({ ...d, source, bootRung: minRung(bootRung(d, subject), d.projectMemory) });
|
|
124
|
+
}
|
|
111
125
|
return dedupeFirstWins(eligible);
|
|
112
126
|
}
|
|
127
|
+
const bootSources = new WeakMap();
|
|
128
|
+
/** The session's boot corpus, each parsed doc joined back to its descriptor.
|
|
129
|
+
* `cachedSubstrateDocs` is a filtered map of `cachedAllMemoryDocs`, and an
|
|
130
|
+
* absolute path names exactly one document in a corpus (store roots are
|
|
131
|
+
* realpath-deduped), so the path is the join key. The join is memoized per
|
|
132
|
+
* cached array because one session renders preferences, knowledge, and any
|
|
133
|
+
* config-change delta off the same corpus. */
|
|
134
|
+
function bootCorpus() {
|
|
135
|
+
const docs = cachedSubstrateDocs(listAllMemoryDocs, parseSubstrateDoc);
|
|
136
|
+
let sources = bootSources.get(docs);
|
|
137
|
+
if (sources === undefined) {
|
|
138
|
+
sources = new Map(cachedAllMemoryDocs(listAllMemoryDocs).map((d) => [d.path, d]));
|
|
139
|
+
bootSources.set(docs, sources);
|
|
140
|
+
}
|
|
141
|
+
return { docs, sources };
|
|
142
|
+
}
|
|
113
143
|
// ---------------------------------------------------------------------------
|
|
114
|
-
//
|
|
144
|
+
// Exposure registration
|
|
115
145
|
// ---------------------------------------------------------------------------
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
function
|
|
121
|
-
if (
|
|
146
|
+
function rendersAtBoot(doc) {
|
|
147
|
+
return isVisible(doc.bootRung) && (doc.bootRung !== 'content' || doc.body.trim() !== '');
|
|
148
|
+
}
|
|
149
|
+
/** Register every visibly rendered winner at its displayed rung. */
|
|
150
|
+
function recordRendered(target, docs) {
|
|
151
|
+
if (target === undefined)
|
|
122
152
|
return;
|
|
123
153
|
for (const d of docs) {
|
|
124
|
-
if (!
|
|
154
|
+
if (!rendersAtBoot(d))
|
|
125
155
|
continue;
|
|
126
|
-
|
|
156
|
+
registerDocumentExposure(target, d.path, d.body, d.bootRung);
|
|
127
157
|
}
|
|
128
158
|
}
|
|
129
159
|
// ---------------------------------------------------------------------------
|
|
@@ -133,14 +163,14 @@ function seedBootDelivery(seen, docs) {
|
|
|
133
163
|
* profile=2, project ranks start at 3 and climb by root nearness — the
|
|
134
164
|
* OUTERMOST ancestor `.crouter/` root ranks lowest (renders first), the
|
|
135
165
|
* NEAREST root ranks highest (renders last) — the mirror of the resolver's
|
|
136
|
-
* nearest-first dedup precedence.
|
|
137
|
-
*
|
|
166
|
+
* nearest-first dedup precedence. Node-scope docs rank last of all (the most
|
|
167
|
+
* specific, this-node-only store). This is display order ONLY — the
|
|
138
168
|
* resolver's REPLACEMENT precedence (first-wins dedup, step 1) is the
|
|
139
169
|
* opposite direction for project/profile: project stack > profile > user >
|
|
140
170
|
* builtin, nearest project strongest; the two orderings are deliberately
|
|
141
171
|
* different (see the module comment). */
|
|
142
|
-
function scopeGeneralityRank(doc
|
|
143
|
-
if (
|
|
172
|
+
function scopeGeneralityRank(doc) {
|
|
173
|
+
if (doc.scope === 'node')
|
|
144
174
|
return 1000;
|
|
145
175
|
if (doc.scope === 'builtin')
|
|
146
176
|
return 0;
|
|
@@ -155,18 +185,17 @@ function scopeGeneralityRank(doc, isNodeLocal) {
|
|
|
155
185
|
const idx = nearFirstIdx === -1 ? 0 : nearFirstIdx;
|
|
156
186
|
return 3 + (roots.length - 1 - idx); // outermost (largest idx) -> smallest rank
|
|
157
187
|
}
|
|
158
|
-
/** The doc's
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
188
|
+
/** The doc's stored physical path segments, `NN-` ordering prefixes intact —
|
|
189
|
+
* the descriptor's store-relative path, which is what lets `tree position`
|
|
190
|
+
* sort respect an author's ordering pin even though the pin is invisible in
|
|
191
|
+
* the canonical name. A directory document's `INDEX` filename names its
|
|
192
|
+
* directory rather than a segment of its own, so it sorts AS its directory,
|
|
193
|
+
* ahead of everything nested under it. */
|
|
164
194
|
function physicalSegments(doc) {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return raw;
|
|
195
|
+
const segments = doc.source.physicalRelativePath.replace(/\.md$/i, '').split('/');
|
|
196
|
+
if (doc.source.representation === 'directory-index')
|
|
197
|
+
segments.pop();
|
|
198
|
+
return segments;
|
|
170
199
|
}
|
|
171
200
|
/** "Tree position" comparator for content-prose ordering within a store:
|
|
172
201
|
* compares two docs' RAW physical segments element-by-element (a shallower
|
|
@@ -193,10 +222,8 @@ function isAgentEditable(doc) {
|
|
|
193
222
|
* display order: `content` docs first, as clean prose (general→specific,
|
|
194
223
|
* then tree position, then filename — no tree chrome); every
|
|
195
224
|
* other winner (preview/name/hidden-bootless) folds into ONE catalog tree
|
|
196
|
-
* exactly as before.
|
|
197
|
-
|
|
198
|
-
* pure resolver render. */
|
|
199
|
-
function renderGrouped(winners, rootLabel, nodeLocalSet = new Set()) {
|
|
225
|
+
* exactly as before. */
|
|
226
|
+
function renderGrouped(winners, rootLabel) {
|
|
200
227
|
const content = [];
|
|
201
228
|
const rest = [];
|
|
202
229
|
for (const d of winners) {
|
|
@@ -205,7 +232,7 @@ function renderGrouped(winners, rootLabel, nodeLocalSet = new Set()) {
|
|
|
205
232
|
else
|
|
206
233
|
rest.push(d);
|
|
207
234
|
}
|
|
208
|
-
content.sort((a, b) => scopeGeneralityRank(a
|
|
235
|
+
content.sort((a, b) => scopeGeneralityRank(a) - scopeGeneralityRank(b) ||
|
|
209
236
|
compareTreePosition(a, b) ||
|
|
210
237
|
a.name.localeCompare(b.name));
|
|
211
238
|
const contentProse = content
|
|
@@ -214,82 +241,40 @@ function renderGrouped(winners, rootLabel, nodeLocalSet = new Set()) {
|
|
|
214
241
|
.join('\n\n');
|
|
215
242
|
return { contentProse, tree: buildTree(rest, rootLabel), contentDocs: content };
|
|
216
243
|
}
|
|
217
|
-
/** The node
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
244
|
+
/** The node's own store (`nodes/<id>/context/memory/`), loaded through the
|
|
245
|
+
* resolver's descriptor loader like every other store, then gate-passed.
|
|
246
|
+
* Returned across ALL kinds — the node store is the catch-all this-node store
|
|
247
|
+
* and rides into the knowledge block.
|
|
248
|
+
*
|
|
249
|
+
* The store is addressed by the EXPLICIT `nodeId` rather than the host
|
|
250
|
+
* process's, because one process renders for other nodes (crtrd claiming a
|
|
251
|
+
* warm spare, `sys prompt-review` walking the canvas). The boot resolver
|
|
252
|
+
* corpus deliberately excludes node scope (`cachedAllMemoryDocs`), so this is
|
|
253
|
+
* the one path that carries node docs into a boot render and they cannot
|
|
254
|
+
* double-surface.
|
|
224
255
|
*
|
|
225
|
-
* Node
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
function
|
|
229
|
-
|
|
230
|
-
|
|
256
|
+
* Node docs follow the same GATE rule as every other store. A doc with no boot
|
|
257
|
+
* entry is retained here; renderKnowledgeForSubject floors it to `name` so it
|
|
258
|
+
* shows as a bare name instead of collapsing into `[+N more]`. */
|
|
259
|
+
function nodeStoreDocs(nodeId, subject) {
|
|
260
|
+
let docs;
|
|
261
|
+
try {
|
|
262
|
+
// Narrowed to the node scope, so cwd and profile select no store.
|
|
263
|
+
docs = loadMemoryTargetView({ cwd: process.cwd(), profileId: null, nodeId }, { scope: 'node', quiet: true }).docs;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
231
266
|
return [];
|
|
267
|
+
}
|
|
232
268
|
const out = [];
|
|
233
|
-
for (const
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
if (!fallbackName)
|
|
237
|
-
continue;
|
|
238
|
-
try {
|
|
239
|
-
const { data, body } = parseFrontmatterGeneric(readText(file));
|
|
240
|
-
const schema = parseSubstrateFrontmatter(data);
|
|
241
|
-
if (schema === null)
|
|
242
|
-
continue;
|
|
243
|
-
// ONE substrate-identity rule (schema.ts's resolveDocName), shared with
|
|
244
|
-
// the resolver and explicit file-context loader: frontmatter `name` wins
|
|
245
|
-
// over the physical-path-derived fallback.
|
|
246
|
-
const name = resolveDocName(data, fallbackName);
|
|
247
|
-
// node-local is NOT a resolver scope; `scope` is a placeholder never read
|
|
248
|
-
// by gate eval (keyed off the NODE subject, not the doc) nor by the
|
|
249
|
-
// renderers (keyed off name / body / rung).
|
|
250
|
-
// Node-local is not a project store: no profile relationship names it,
|
|
251
|
-
// so it carries the neutral ceiling.
|
|
252
|
-
out.push({ ...schema, name, scope: 'user', path: file, body, projectMemory: NEUTRAL_PROJECT_MEMORY });
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
// A single malformed file is skipped, never fatal to the render.
|
|
269
|
+
for (const source of docs) {
|
|
270
|
+
const parsed = parseSubstrateDoc(source);
|
|
271
|
+
if (parsed === null || !gatePasses(parsed, subject))
|
|
256
272
|
continue;
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
function newDir(path) {
|
|
262
|
-
const segs = path.split('/');
|
|
263
|
-
return {
|
|
264
|
-
segment: path === '' ? '' : segs[segs.length - 1],
|
|
265
|
-
path,
|
|
266
|
-
children: new Map(),
|
|
267
|
-
leaves: [],
|
|
268
|
-
hiddenHere: 0,
|
|
269
|
-
renders: false,
|
|
270
|
-
ownCount: 0,
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
/** Walk/create the dir node at `path`, building intermediates. */
|
|
274
|
-
function ensureDir(root, path) {
|
|
275
|
-
if (path === '')
|
|
276
|
-
return root;
|
|
277
|
-
let cur = root;
|
|
278
|
-
let acc = '';
|
|
279
|
-
for (const s of path.split('/')) {
|
|
280
|
-
acc = acc === '' ? s : `${acc}/${s}`;
|
|
281
|
-
let child = cur.children.get(s);
|
|
282
|
-
if (!child) {
|
|
283
|
-
child = newDir(acc);
|
|
284
|
-
cur.children.set(s, child);
|
|
285
|
-
}
|
|
286
|
-
cur = child;
|
|
273
|
+
const rung = bootRung(parsed, subject);
|
|
274
|
+
const floored = isVisible(rung) || parsed.surfaces.some((e) => e.on === 'boot') ? rung : 'name';
|
|
275
|
+
out.push({ ...parsed, source, bootRung: floored });
|
|
287
276
|
}
|
|
288
|
-
return
|
|
289
|
-
}
|
|
290
|
-
function parentDirOf(name) {
|
|
291
|
-
const i = name.lastIndexOf('/');
|
|
292
|
-
return i === -1 ? '' : name.slice(0, i);
|
|
277
|
+
return out;
|
|
293
278
|
}
|
|
294
279
|
function leafSegment(name) {
|
|
295
280
|
const i = name.lastIndexOf('/');
|
|
@@ -298,74 +283,89 @@ function leafSegment(name) {
|
|
|
298
283
|
function isVisible(rung) {
|
|
299
284
|
return rungRank(rung) >= rungRank('name');
|
|
300
285
|
}
|
|
286
|
+
/** Project one canonical tree node into a render row. `byPath` maps a
|
|
287
|
+
* descriptor back to the winner carrying its boot rung. */
|
|
288
|
+
function planDir(node, byPath) {
|
|
289
|
+
const children = node.childDirectories.map((child) => planDir(child, byPath));
|
|
290
|
+
const leaves = [];
|
|
291
|
+
let hiddenHere = 0;
|
|
292
|
+
for (const member of node.childDocuments) {
|
|
293
|
+
const doc = byPath.get(member.document.path);
|
|
294
|
+
if (doc === undefined)
|
|
295
|
+
continue;
|
|
296
|
+
if (isVisible(doc.bootRung))
|
|
297
|
+
leaves.push({ name: member.name, order: member.order, doc });
|
|
298
|
+
else
|
|
299
|
+
hiddenHere += 1;
|
|
300
|
+
}
|
|
301
|
+
const ownDoc = node.document === null ? null : byPath.get(node.document.path) ?? null;
|
|
302
|
+
let own = null;
|
|
303
|
+
if (ownDoc !== null) {
|
|
304
|
+
if (isVisible(ownDoc.bootRung))
|
|
305
|
+
own = ownDoc;
|
|
306
|
+
else
|
|
307
|
+
hiddenHere += 1;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
name: node.name,
|
|
311
|
+
segment: leafSegment(node.name),
|
|
312
|
+
order: node.order,
|
|
313
|
+
own,
|
|
314
|
+
children,
|
|
315
|
+
leaves,
|
|
316
|
+
hiddenHere,
|
|
317
|
+
renders: false,
|
|
318
|
+
ownCount: 0,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
301
321
|
/** Bottom-up pass: decide which dirs render and where hidden counts land. A dir
|
|
302
|
-
* renders when it
|
|
303
|
-
*
|
|
304
|
-
* is visible — the root itself
|
|
305
|
-
* NON-rendering dir bubble up to the
|
|
306
|
-
* a rendering dir keeps its own
|
|
322
|
+
* renders when it discloses something visible (its own document or ≥1 visible
|
|
323
|
+
* leaf), when ≥1 child dir renders, or when it is top-level (a direct child of
|
|
324
|
+
* the root always renders so its subtree count is visible — the root itself
|
|
325
|
+
* always renders). Bootless docs in a NON-rendering dir bubble up to the
|
|
326
|
+
* nearest rendering ancestor's `[+N more]`; a rendering dir keeps its own
|
|
327
|
+
* subtree count. Returns the count to bubble up. */
|
|
307
328
|
function resolveDir(dir, isTopLevel) {
|
|
308
329
|
let totalHidden = dir.hiddenHere;
|
|
309
|
-
for (const child of dir.children
|
|
310
|
-
totalHidden += resolveDir(child, dir.
|
|
330
|
+
for (const child of dir.children) {
|
|
331
|
+
totalHidden += resolveDir(child, dir.name === '');
|
|
311
332
|
}
|
|
312
|
-
const hasVisibleLeaf = dir.leaves.length > 0;
|
|
313
|
-
const anyChildRenders =
|
|
314
|
-
dir.renders = hasVisibleLeaf || anyChildRenders || isTopLevel || dir.
|
|
333
|
+
const hasVisibleLeaf = dir.own !== null || dir.leaves.length > 0;
|
|
334
|
+
const anyChildRenders = dir.children.some((c) => c.renders);
|
|
335
|
+
dir.renders = hasVisibleLeaf || anyChildRenders || isTopLevel || dir.name === '';
|
|
315
336
|
dir.ownCount = dir.renders ? totalHidden : 0;
|
|
316
337
|
return dir.renders ? 0 : totalHidden;
|
|
317
338
|
}
|
|
318
|
-
function
|
|
339
|
+
function itemOrder(item) {
|
|
319
340
|
if (item.kind === 'dir')
|
|
320
|
-
return item.node.
|
|
321
|
-
|
|
322
|
-
return leafSegment(item.doc.name);
|
|
323
|
-
return '';
|
|
341
|
+
return item.node.order;
|
|
342
|
+
return item.kind === 'leaf' ? item.entry.order : '';
|
|
324
343
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
* would drop them mid-list. */
|
|
330
|
-
function isIndexPinned(item) {
|
|
331
|
-
return item.kind === 'leaf' && leafSegment(item.doc.name) === 'INDEX';
|
|
344
|
+
function itemName(item) {
|
|
345
|
+
if (item.kind === 'dir')
|
|
346
|
+
return item.node.name;
|
|
347
|
+
return item.kind === 'leaf' ? item.entry.name : '';
|
|
332
348
|
}
|
|
333
|
-
/** Render one
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
break;
|
|
340
|
-
}
|
|
341
|
-
case 'content': {
|
|
342
|
-
lines.push(`${entryPrefix}${label}`);
|
|
343
|
-
const body = doc.body.trim();
|
|
344
|
-
if (body !== '') {
|
|
345
|
-
for (const raw of body.split('\n')) {
|
|
346
|
-
lines.push(`${childPrefix} ${raw}`.replace(/\s+$/, ''));
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
break;
|
|
350
|
-
}
|
|
351
|
-
default: // 'name'
|
|
352
|
-
lines.push(`${entryPrefix}${label}`);
|
|
353
|
-
}
|
|
349
|
+
/** Render one row's label at its document's boot rung: `preview` appends the
|
|
350
|
+
* read-when routing line, `name` leaves the bare label. A `content` winner
|
|
351
|
+
* never reaches the tree — renderGrouped renders its body as prose instead. */
|
|
352
|
+
function renderDocEntry(doc, label, entryPrefix, lines) {
|
|
353
|
+
const preview = doc.bootRung === 'preview' ? previewLine(doc) : '';
|
|
354
|
+
lines.push(preview === '' ? `${entryPrefix}${label}` : `${entryPrefix}${label} # read when: ${preview}`);
|
|
354
355
|
}
|
|
355
356
|
/** Render the children of a rendering dir, with `childPrefix` carrying the tree
|
|
356
|
-
* guides for this depth. Order:
|
|
357
|
-
*
|
|
358
|
-
*
|
|
357
|
+
* guides for this depth. Order: child dirs and leaves intermixed by stored
|
|
358
|
+
* physical position, then the `[+N more]` count last. A dir that carries its
|
|
359
|
+
* own document renders that document's entry on the dir's own line. */
|
|
359
360
|
function renderChildren(dir, childPrefix, lines) {
|
|
360
361
|
const ordered = [];
|
|
361
|
-
for (const child of dir.children
|
|
362
|
+
for (const child of dir.children) {
|
|
362
363
|
if (child.renders)
|
|
363
364
|
ordered.push({ kind: 'dir', node: child });
|
|
364
365
|
}
|
|
365
|
-
for (const
|
|
366
|
-
ordered.push({ kind: 'leaf',
|
|
367
|
-
ordered.sort((a, b) =>
|
|
368
|
-
itemSortKey(a).localeCompare(itemSortKey(b)));
|
|
366
|
+
for (const entry of dir.leaves)
|
|
367
|
+
ordered.push({ kind: 'leaf', entry });
|
|
368
|
+
ordered.sort((a, b) => itemOrder(a).localeCompare(itemOrder(b)) || itemName(a).localeCompare(itemName(b)));
|
|
369
369
|
if (dir.ownCount > 0)
|
|
370
370
|
ordered.push({ kind: 'more', count: dir.ownCount });
|
|
371
371
|
ordered.forEach((item, i) => {
|
|
@@ -376,12 +376,17 @@ function renderChildren(dir, childPrefix, lines) {
|
|
|
376
376
|
case 'more':
|
|
377
377
|
lines.push(`${entryPrefix}[+${item.count} more]`);
|
|
378
378
|
break;
|
|
379
|
-
case 'dir':
|
|
380
|
-
|
|
379
|
+
case 'dir': {
|
|
380
|
+
const label = `${item.node.segment}/`;
|
|
381
|
+
if (item.node.own === null)
|
|
382
|
+
lines.push(`${entryPrefix}${label}`);
|
|
383
|
+
else
|
|
384
|
+
renderDocEntry(item.node.own, label, entryPrefix, lines);
|
|
381
385
|
renderChildren(item.node, nextPrefix, lines);
|
|
382
386
|
break;
|
|
387
|
+
}
|
|
383
388
|
case 'leaf':
|
|
384
|
-
renderDocEntry(item.doc, leafSegment(item.
|
|
389
|
+
renderDocEntry(item.entry.doc, leafSegment(item.entry.name), entryPrefix, lines);
|
|
385
390
|
break;
|
|
386
391
|
}
|
|
387
392
|
});
|
|
@@ -390,26 +395,18 @@ function renderChildren(dir, childPrefix, lines) {
|
|
|
390
395
|
* headed by `rootLabel`. Returns '' when there are no docs at all (the
|
|
391
396
|
* empty-tree contract).
|
|
392
397
|
*
|
|
393
|
-
* Callers (renderGrouped) pass docs
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
397
|
-
* happens upstream of this call, never before dedup. */
|
|
398
|
+
* Callers (renderGrouped) pass docs already first-wins deduped by step 1
|
|
399
|
+
* (selectWinners); merging them into the canonical tree keeps that first-wins
|
|
400
|
+
* outcome structurally, because a node's `document` is the first candidate at
|
|
401
|
+
* its identity in source order. */
|
|
398
402
|
function buildTree(docs, rootLabel) {
|
|
399
403
|
if (docs.length === 0)
|
|
400
404
|
return '';
|
|
401
|
-
const
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
seen.add(d.name);
|
|
407
|
-
const parent = ensureDir(root, parentDirOf(d.name));
|
|
408
|
-
if (isVisible(d.bootRung))
|
|
409
|
-
parent.leaves.push(d);
|
|
410
|
-
else
|
|
411
|
-
parent.hiddenHere += 1;
|
|
412
|
-
}
|
|
405
|
+
const byPath = new Map();
|
|
406
|
+
for (const d of docs)
|
|
407
|
+
if (!byPath.has(d.path))
|
|
408
|
+
byPath.set(d.path, d);
|
|
409
|
+
const root = planDir(buildMemoryTree(docs.map((d) => d.source)).root, byPath);
|
|
413
410
|
resolveDir(root, false);
|
|
414
411
|
const lines = [rootLabel];
|
|
415
412
|
renderChildren(root, '', lines);
|
|
@@ -476,7 +473,7 @@ function subKindDocCitation(subKind) {
|
|
|
476
473
|
* Returns '' when the kind has zero available sub-kinds (no dangling empty
|
|
477
474
|
* header). Emitted for BOTH base and orchestrator modes per the CTO's ruling —
|
|
478
475
|
* mode is irrelevant to which sub-kinds are spawnable. */
|
|
479
|
-
function buildSubPersonaMenu(kind) {
|
|
476
|
+
export function buildSubPersonaMenu(kind) {
|
|
480
477
|
const subKinds = subKindsAvailableTo(kind);
|
|
481
478
|
if (subKinds.length === 0)
|
|
482
479
|
return '';
|
|
@@ -512,14 +509,14 @@ function buildSubPersonaMenu(kind) {
|
|
|
512
509
|
* across nodes is preserved. ALWAYS returns a non-empty splice — the guidance
|
|
513
510
|
* frame is unconditional, so it is never empty even when no preference is
|
|
514
511
|
* eligible. */
|
|
515
|
-
function buildPreferencesBlock(subject,
|
|
512
|
+
function buildPreferencesBlock(subject, target) {
|
|
516
513
|
let contentDocs = [];
|
|
517
514
|
let tree = '';
|
|
518
515
|
let protocol = '';
|
|
519
516
|
let memBody = MEMORY_USAGE_GUIDANCE;
|
|
520
517
|
if (subject !== null) {
|
|
521
518
|
const winners = selectWinners(subject, 'preference');
|
|
522
|
-
|
|
519
|
+
recordRendered(target, winners);
|
|
523
520
|
const grouped = renderGrouped(winners, 'preferences');
|
|
524
521
|
contentDocs = grouped.contentDocs;
|
|
525
522
|
tree = grouped.tree;
|
|
@@ -548,12 +545,22 @@ function buildPreferencesBlock(subject, seen) {
|
|
|
548
545
|
const memoryBlock = `<memory kind="preference">\n${memBody}\n</memory>`;
|
|
549
546
|
return { block: protocol === '' ? memoryBlock : `${protocol}\n\n${memoryBlock}`, contentDocs, tree };
|
|
550
547
|
}
|
|
551
|
-
export function renderPreferencesForSubject(subject,
|
|
552
|
-
return buildPreferencesBlock(subject,
|
|
548
|
+
export function renderPreferencesForSubject(subject, target) {
|
|
549
|
+
return buildPreferencesBlock(subject, target);
|
|
553
550
|
}
|
|
554
551
|
// ---------------------------------------------------------------------------
|
|
555
552
|
// 2. Knowledge — `<memory kind="knowledge">` (the first-message bearings).
|
|
556
553
|
// ---------------------------------------------------------------------------
|
|
554
|
+
/** STEP 1 for the knowledge block, shared by the boot render and the
|
|
555
|
+
* config-change delta: the resolver's knowledge winners ∪ the node's own store
|
|
556
|
+
* (any kind), first-wins deduped resolver-first so a same-named node doc
|
|
557
|
+
* loses. Node docs carry the one rung floor — a node doc with NO boot entry is
|
|
558
|
+
* bumped to `name` so it surfaces as a bare name instead of collapsing into a
|
|
559
|
+
* `[+N more]` count; a node doc whose boot entries all fail their gates stays
|
|
560
|
+
* absent. */
|
|
561
|
+
function knowledgeWinners(subject, nodeId) {
|
|
562
|
+
return dedupeFirstWins([...selectWinners(subject, 'knowledge'), ...nodeStoreDocs(nodeId, subject)]);
|
|
563
|
+
}
|
|
557
564
|
/** The `<memory kind="knowledge">` block embedded in the session_start bearings
|
|
558
565
|
* message (bearings.ts pushes it as a SIBLING of `<crtr-bearings>`, or drops it
|
|
559
566
|
* when ''). The consultable catalog: every eligible `kind: knowledge` resolver
|
|
@@ -567,17 +574,10 @@ export function renderPreferencesForSubject(subject, seen) {
|
|
|
567
574
|
* docs never reach the `content` rung). Procedural guidance and factual
|
|
568
575
|
* references both live here as `knowledge`. Returns '' when nothing is
|
|
569
576
|
* eligible. */
|
|
570
|
-
export function renderKnowledgeForSubject(subject, nodeId,
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
});
|
|
575
|
-
const nodeLocalSet = new Set(nodeLocal);
|
|
576
|
-
// Resolver winners first: first-wins dedup keeps the resolver's copy over a
|
|
577
|
-
// same-named node-local doc, matching the original precedence.
|
|
578
|
-
const winners = dedupeFirstWins([...selectWinners(subject, 'knowledge'), ...nodeLocal]);
|
|
579
|
-
seedBootDelivery(seen, winners);
|
|
580
|
-
const { contentProse, tree } = renderGrouped(winners, 'knowledge', nodeLocalSet);
|
|
577
|
+
export function renderKnowledgeForSubject(subject, nodeId, target) {
|
|
578
|
+
const winners = knowledgeWinners(subject, nodeId);
|
|
579
|
+
recordRendered(target, winners);
|
|
580
|
+
const { contentProse, tree } = renderGrouped(winners, 'knowledge');
|
|
581
581
|
if (contentProse === '' && tree === '')
|
|
582
582
|
return '';
|
|
583
583
|
let out = KNOWLEDGE_INTRO;
|
|
@@ -589,3 +589,71 @@ export function renderKnowledgeForSubject(subject, nodeId, seen) {
|
|
|
589
589
|
// Surfaced INTO this node: swap literal `$CRTR_CONTEXT_DIR` for the real path.
|
|
590
590
|
return interpolateNodePaths(`<memory kind="knowledge">\n${out}\n</memory>`, nodeId);
|
|
591
591
|
}
|
|
592
|
+
// ---------------------------------------------------------------------------
|
|
593
|
+
// 3. The config-change delta — the memory a node's NEW configuration exposes.
|
|
594
|
+
// ---------------------------------------------------------------------------
|
|
595
|
+
const DELTA_PROTOCOL_INTRO = 'Your node configuration changed. This operating protocol is in force for you now:';
|
|
596
|
+
const DELTA_PREFERENCES_INTRO = 'Your new configuration puts these preferences in force — standing directives and corrections ' +
|
|
597
|
+
'from the user. To read one, run `crtr memory read <name>`. ' +
|
|
598
|
+
READ_LEGEND.replace('%s', 'preference');
|
|
599
|
+
const DELTA_KNOWLEDGE_INTRO = 'Your new configuration exposes these knowledge documents — consult them exactly like the rest ' +
|
|
600
|
+
'of your catalog (`crtr memory read <name>`). ' +
|
|
601
|
+
READ_LEGEND.replace('%s', 'document');
|
|
602
|
+
/** Winners not yet loaded at their boot rung. Bootless winners remain absent. */
|
|
603
|
+
function undelivered(target, docs) {
|
|
604
|
+
return docs.filter((d) => rendersAtBoot(d) && !documentExposedAtOrAbove(target.state, d.path, d.body, d.bootRung));
|
|
605
|
+
}
|
|
606
|
+
/** What a node's configuration change newly exposes, rendered as up to three
|
|
607
|
+
* blocks in one string: the shipped protocol prose, a `<memory kind="preference">`
|
|
608
|
+
* block, and a `<memory kind="knowledge">` block — each dropped when empty, ''
|
|
609
|
+
* when the change exposes nothing.
|
|
610
|
+
*
|
|
611
|
+
* Both kinds use the current subject's ordinary winner assembly, then compare
|
|
612
|
+
* against the active exposure maximum. A lower rung or removal yields nothing
|
|
613
|
+
* because content already loaded into this context cannot be withdrawn.
|
|
614
|
+
* Rendered deltas register as transcript exposure through `target`. */
|
|
615
|
+
export function renderConfigChangeDelta(subject, nodeId, target) {
|
|
616
|
+
const prefs = undelivered(target, selectWinners(subject, 'preference'));
|
|
617
|
+
const knowledgeDelta = undelivered(target, knowledgeWinners(subject, nodeId));
|
|
618
|
+
recordRendered(target, [...prefs, ...knowledgeDelta]);
|
|
619
|
+
const { contentDocs, tree } = renderGrouped(prefs, 'preferences');
|
|
620
|
+
// Same split as the boot preference render: shipped bodies are protocol prose
|
|
621
|
+
// (the rules of the node's world), agent-editable bodies are memory the node
|
|
622
|
+
// can revise, and every non-content preference winner rides the one catalog.
|
|
623
|
+
const shipped = contentDocs
|
|
624
|
+
.filter((d) => !isAgentEditable(d))
|
|
625
|
+
.map((d) => d.body.trim())
|
|
626
|
+
.filter((b) => b !== '');
|
|
627
|
+
const editable = contentDocs
|
|
628
|
+
.filter((d) => isAgentEditable(d))
|
|
629
|
+
.map((d) => {
|
|
630
|
+
const b = d.body.trim();
|
|
631
|
+
return b === '' ? '' : `<preference name="${d.name}">\n${b}\n</preference>`;
|
|
632
|
+
})
|
|
633
|
+
.filter((b) => b !== '');
|
|
634
|
+
const blocks = [];
|
|
635
|
+
if (shipped.length > 0)
|
|
636
|
+
blocks.push(`${DELTA_PROTOCOL_INTRO}\n\n${shipped.join('\n\n')}`);
|
|
637
|
+
if (editable.length > 0 || tree !== '') {
|
|
638
|
+
let body = DELTA_PREFERENCES_INTRO;
|
|
639
|
+
if (editable.length > 0)
|
|
640
|
+
body += `\n\n${editable.join('\n\n')}`;
|
|
641
|
+
if (tree !== '')
|
|
642
|
+
body += `\n\n${tree}`;
|
|
643
|
+
body += `\n\n${PREFERENCES_OUTRO}`;
|
|
644
|
+
blocks.push(`<memory kind="preference">\n${body}\n</memory>`);
|
|
645
|
+
}
|
|
646
|
+
const knowledge = renderGrouped(knowledgeDelta, 'knowledge');
|
|
647
|
+
if (knowledge.contentProse !== '' || knowledge.tree !== '') {
|
|
648
|
+
let body = DELTA_KNOWLEDGE_INTRO;
|
|
649
|
+
if (knowledge.contentProse !== '')
|
|
650
|
+
body += `\n\n${knowledge.contentProse}`;
|
|
651
|
+
if (knowledge.tree !== '')
|
|
652
|
+
body += `\n\n${knowledge.tree}`;
|
|
653
|
+
body += `\n\n${KNOWLEDGE_OUTRO}`;
|
|
654
|
+
blocks.push(`<memory kind="knowledge">\n${body}\n</memory>`);
|
|
655
|
+
}
|
|
656
|
+
if (blocks.length === 0)
|
|
657
|
+
return '';
|
|
658
|
+
return interpolateNodePaths(blocks.join('\n\n'), nodeId);
|
|
659
|
+
}
|