@crouton-kit/crouter 0.3.37 → 0.3.39
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/build-root.js +1 -0
- package/dist/{builtin-personas/runtime-base.md → builtin-memory/00-runtime-base.md} +7 -0
- package/dist/{builtin-personas/spine/has-manager.md → builtin-memory/01-spine/00-has-manager.md} +8 -0
- package/dist/builtin-memory/01-spine/01-no-manager.md +10 -0
- package/dist/{builtin-personas/lifecycle/terminal.md → builtin-memory/02-lifecycle/00-terminal.md} +8 -0
- package/dist/{builtin-personas/lifecycle/resident.md → builtin-memory/02-lifecycle/01-resident.md} +8 -0
- package/dist/{builtin-personas/waiting.md → builtin-memory/03-waiting.md} +7 -0
- package/dist/{builtin-personas/orchestration-kernel.md → builtin-memory/04-orchestration-kernel.md} +8 -0
- package/dist/{builtin-personas/advisor/PERSONA.md → builtin-memory/05-kinds/advisor/00-base.md} +5 -2
- package/dist/{builtin-personas/design/PERSONA.md → builtin-memory/05-kinds/design/00-base.md} +5 -2
- package/dist/{builtin-personas/design/orchestrator.md → builtin-memory/05-kinds/design/01-orchestrator.md} +5 -4
- package/dist/{builtin-personas/developer/PERSONA.md → builtin-memory/05-kinds/developer/00-base.md} +5 -2
- package/dist/{builtin-personas/developer/orchestrator.md → builtin-memory/05-kinds/developer/01-orchestrator.md} +5 -4
- package/dist/{builtin-personas/explore/PERSONA.md → builtin-memory/05-kinds/explore/00-base.md} +5 -2
- package/dist/{builtin-personas/explore/orchestrator.md → builtin-memory/05-kinds/explore/01-orchestrator.md} +5 -3
- package/dist/{builtin-personas/general/PERSONA.md → builtin-memory/05-kinds/general/00-base.md} +5 -2
- package/dist/{builtin-personas/general/orchestrator.md → builtin-memory/05-kinds/general/01-orchestrator.md} +5 -3
- package/dist/{builtin-personas/plan/PERSONA.md → builtin-memory/05-kinds/plan/00-base.md} +5 -2
- package/dist/{builtin-personas/plan/orchestrator.md → builtin-memory/05-kinds/plan/01-orchestrator.md} +5 -4
- package/dist/{builtin-personas/plan/reviewers/architecture-fit/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/architecture-fit.md} +5 -2
- package/dist/{builtin-personas/plan/reviewers/code-smells/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/code-smells.md} +5 -2
- package/dist/{builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md} +5 -2
- package/dist/{builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md} +5 -2
- package/dist/{builtin-personas/plan/reviewers/security/PERSONA.md → builtin-memory/05-kinds/plan/reviewers/security.md} +5 -2
- package/dist/{builtin-personas/product/PERSONA.md → builtin-memory/05-kinds/product/00-base.md} +5 -2
- package/dist/{builtin-personas/product/orchestrator.md → builtin-memory/05-kinds/product/01-orchestrator.md} +5 -4
- package/dist/{builtin-personas/product/teardown/PERSONA.md → builtin-memory/05-kinds/product/teardown.md} +5 -3
- package/dist/{builtin-personas/review/PERSONA.md → builtin-memory/05-kinds/review/00-base.md} +5 -2
- package/dist/{builtin-personas/review/orchestrator.md → builtin-memory/05-kinds/review/01-orchestrator.md} +5 -3
- package/dist/{builtin-personas/spec/PERSONA.md → builtin-memory/05-kinds/spec/00-base.md} +5 -2
- package/dist/{builtin-personas/spec/orchestrator.md → builtin-memory/05-kinds/spec/01-orchestrator.md} +5 -4
- package/dist/{builtin-personas/spec/requirements/PERSONA.md → builtin-memory/05-kinds/spec/requirements.md} +5 -2
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
- package/dist/builtin-views/prompt-review/core.mjs +6 -134
- package/dist/builtin-views/settings/core.mjs +26 -94
- package/dist/builtin-views/settings/text.mjs +0 -5
- package/dist/builtin-views/settings/tui.mjs +1 -7
- package/dist/builtin-views/settings/web.jsx +3 -49
- package/dist/clients/attach/attach-cmd.js +416 -416
- package/dist/commands/memory/read.js +3 -3
- package/dist/commands/memory/shared.d.ts +13 -8
- package/dist/commands/memory/shared.js +22 -8
- package/dist/commands/memory/write.js +5 -3
- package/dist/commands/node.js +40 -11
- package/dist/commands/profile/add-project.d.ts +1 -0
- package/dist/commands/profile/add-project.js +42 -0
- package/dist/commands/profile/delete.d.ts +1 -0
- package/dist/commands/profile/delete.js +39 -0
- package/dist/commands/profile/list.d.ts +1 -0
- package/dist/commands/profile/list.js +35 -0
- package/dist/commands/profile/new.d.ts +1 -0
- package/dist/commands/profile/new.js +50 -0
- package/dist/commands/profile/remove-project.d.ts +1 -0
- package/dist/commands/profile/remove-project.js +42 -0
- package/dist/commands/profile/rename.d.ts +1 -0
- package/dist/commands/profile/rename.js +42 -0
- package/dist/commands/profile/show.d.ts +1 -0
- package/dist/commands/profile/show.js +50 -0
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/profile.js +33 -0
- package/dist/commands/sys/config.js +6 -21
- package/dist/commands/sys/prompt-review.js +90 -17
- package/dist/core/canvas/canvas.js +10 -9
- package/dist/core/canvas/db.js +11 -0
- package/dist/core/canvas/history.js +1 -0
- package/dist/core/canvas/types.d.ts +10 -0
- package/dist/core/config.d.ts +44 -1
- package/dist/core/config.js +155 -12
- package/dist/core/hearth/provider.d.ts +8 -0
- package/dist/core/hearth/providers/__tests__/sweep-and-release.test.d.ts +1 -0
- package/dist/core/hearth/providers/__tests__/sweep-and-release.test.js +254 -0
- package/dist/core/hearth/providers/blaxel-home.d.ts +39 -0
- package/dist/core/hearth/providers/blaxel-home.js +271 -21
- package/dist/core/hearth/providers/blaxel.d.ts +5 -0
- package/dist/core/hearth/providers/blaxel.js +86 -4
- package/dist/core/hearth/providers/types.d.ts +16 -0
- package/dist/core/memory-resolver.d.ts +26 -15
- package/dist/core/memory-resolver.js +158 -40
- package/dist/core/profiles/manifest.d.ts +33 -0
- package/dist/core/profiles/manifest.js +332 -0
- package/dist/core/profiles/select.d.ts +11 -0
- package/dist/core/profiles/select.js +115 -0
- package/dist/core/runtime/bearings.d.ts +9 -0
- package/dist/core/runtime/bearings.js +31 -0
- package/dist/core/runtime/front-door.js +9 -2
- package/dist/core/runtime/launch.d.ts +25 -7
- package/dist/core/runtime/launch.js +42 -55
- package/dist/core/runtime/nodes.d.ts +7 -0
- package/dist/core/runtime/nodes.js +6 -0
- package/dist/core/runtime/persona.js +18 -32
- package/dist/core/runtime/spawn.d.ts +13 -0
- package/dist/core/runtime/spawn.js +25 -0
- package/dist/core/scope.d.ts +14 -7
- package/dist/core/scope.js +76 -20
- package/dist/core/substrate/index.d.ts +3 -3
- package/dist/core/substrate/index.js +5 -3
- package/dist/core/substrate/on-read.js +3 -2
- package/dist/core/substrate/render.d.ts +16 -11
- package/dist/core/substrate/render.js +249 -62
- package/dist/core/substrate/schema.d.ts +11 -3
- package/dist/core/substrate/schema.js +25 -0
- package/dist/core/substrate/subject.d.ts +15 -2
- package/dist/core/substrate/subject.js +24 -2
- package/dist/hearth/control-plane/__tests__/error-serialization.test.d.ts +1 -0
- package/dist/hearth/control-plane/__tests__/error-serialization.test.js +29 -0
- package/dist/hearth/control-plane/__tests__/oauth-serving-marker.test.d.ts +1 -0
- package/dist/hearth/control-plane/__tests__/oauth-serving-marker.test.js +44 -0
- package/dist/hearth/control-plane/__tests__/wake-roll.test.d.ts +1 -0
- package/dist/hearth/control-plane/__tests__/wake-roll.test.js +230 -0
- package/dist/hearth/control-plane/db.js +27 -0
- package/dist/hearth/control-plane/hearth-target.d.ts +23 -0
- package/dist/hearth/control-plane/hearth-target.js +68 -0
- package/dist/hearth/control-plane/registry.d.ts +6 -1
- package/dist/hearth/control-plane/registry.js +7 -0
- package/dist/hearth/control-plane/relay.d.ts +4 -0
- package/dist/hearth/control-plane/relay.js +72 -3
- package/dist/hearth/control-plane/secrets.d.ts +14 -0
- package/dist/hearth/control-plane/secrets.js +21 -0
- package/dist/hearth/control-plane/server.js +140 -4
- package/dist/hearth/control-plane/serving.d.ts +15 -0
- package/dist/hearth/control-plane/serving.js +106 -0
- package/dist/hearth/control-plane/types.d.ts +20 -0
- package/dist/hearth/control-plane/wake.d.ts +8 -2
- package/dist/hearth/control-plane/wake.js +241 -3
- package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
- package/dist/types.d.ts +57 -1
- package/dist/types.js +83 -1
- package/dist/web-client/assets/{index-MSKSWwV0.js → index-CbO8L0mN.js} +4 -4
- package/dist/web-client/assets/index-DwO46Cs5.css +2 -0
- package/dist/web-client/index.html +2 -2
- package/package.json +2 -2
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +0 -56
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +0 -57
- package/dist/builtin-memory/crouter-development/personas.md +0 -112
- package/dist/builtin-personas/spine/no-manager.md +0 -2
- package/dist/core/personas/index.d.ts +0 -13
- package/dist/core/personas/index.js +0 -11
- package/dist/core/personas/loader.d.ts +0 -159
- package/dist/core/personas/loader.js +0 -327
- package/dist/core/personas/resolve.d.ts +0 -59
- package/dist/core/personas/resolve.js +0 -376
- package/dist/web-client/assets/index-DUThOUzU.css +0 -2
|
@@ -7,15 +7,13 @@
|
|
|
7
7
|
// for a refresh-yield.
|
|
8
8
|
// The spec is rewritten on every polymorph (base→orchestrator) so a node
|
|
9
9
|
// always comes back as its *current* self.
|
|
10
|
-
import { existsSync, mkdirSync,
|
|
10
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { dirname, join } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
|
-
import {
|
|
14
|
-
import { resolve as resolvePersona } from '../personas/index.js';
|
|
13
|
+
import { readMergedLaunchConfig, resolveKindConfig } from '../config.js';
|
|
15
14
|
import { nodeEnv } from './nodes.js';
|
|
16
15
|
import { editorLabel } from '../canvas/index.js';
|
|
17
16
|
import { nodeDir } from '../canvas/paths.js';
|
|
18
|
-
import { defaultScopeConfig } from '../../types.js';
|
|
19
17
|
// ---------------------------------------------------------------------------
|
|
20
18
|
// The two canvas pi-extensions every node loads. They self-gate on the live
|
|
21
19
|
// {kind,mode} env, so the worker→orchestrator polymorph flips hook behavior
|
|
@@ -71,37 +69,13 @@ export const CANVAS_EXTENSIONS = [
|
|
|
71
69
|
// xhigh — confirmed via `pi --help`), so the resolved spec is exactly the
|
|
72
70
|
// string buildPiArgv passes to `--model`; no extra launch plumbing.
|
|
73
71
|
// ---------------------------------------------------------------------------
|
|
74
|
-
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const path = userConfigPath('user');
|
|
80
|
-
if (path === null)
|
|
81
|
-
return defaultScopeConfig();
|
|
82
|
-
try {
|
|
83
|
-
const stat = statSync(path);
|
|
84
|
-
if (cachedUserConfig !== null &&
|
|
85
|
-
cachedUserConfig.path === path &&
|
|
86
|
-
cachedUserConfig.mtimeMs === stat.mtimeMs &&
|
|
87
|
-
cachedUserConfig.size === stat.size) {
|
|
88
|
-
return cachedUserConfig.config;
|
|
89
|
-
}
|
|
90
|
-
const config = readConfig('user');
|
|
91
|
-
cachedUserConfig = { path, mtimeMs: stat.mtimeMs, size: stat.size, config };
|
|
92
|
-
return config;
|
|
93
|
-
}
|
|
94
|
-
catch (error) {
|
|
95
|
-
if (error.code === 'ENOENT') {
|
|
96
|
-
cachedUserConfig = null;
|
|
97
|
-
return defaultScopeConfig();
|
|
98
|
-
}
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const DEFAULT_MODEL_LADDERS = defaultScopeConfig().modelLadders;
|
|
72
|
+
/** Model-ladder resolution goes through `readMergedLaunchConfig` — the SAME
|
|
73
|
+
* project-stack > profile > user > builtin precedence `resolveKindConfig`
|
|
74
|
+
* already uses for `kinds` — so a project/profile override of `modelLadders`
|
|
75
|
+
* is honored identically to a kind override, instead of this file reading
|
|
76
|
+
* only user config. */
|
|
103
77
|
function modelLadders() {
|
|
104
|
-
return
|
|
78
|
+
return readMergedLaunchConfig().modelLadders;
|
|
105
79
|
}
|
|
106
80
|
function ladderFor(provider, ladders = modelLadders()) {
|
|
107
81
|
return ladders[provider];
|
|
@@ -112,7 +86,7 @@ function ladderFor(provider, ladders = modelLadders()) {
|
|
|
112
86
|
* defaultProvider in config.json wins over this env. */
|
|
113
87
|
export const OPENAI_PROVIDER_ENV = 'CRTR_MODEL_PROVIDER';
|
|
114
88
|
function defaultProvider() {
|
|
115
|
-
const cfgProvider =
|
|
89
|
+
const cfgProvider = readMergedLaunchConfig().modelLadders.defaultProvider;
|
|
116
90
|
if (cfgProvider === 'anthropic' || cfgProvider === 'openai')
|
|
117
91
|
return cfgProvider;
|
|
118
92
|
return (process.env[OPENAI_PROVIDER_ENV] ?? '').toLowerCase() === 'openai' ? 'openai' : 'anthropic';
|
|
@@ -198,31 +172,44 @@ export function normalizeModel(model) {
|
|
|
198
172
|
// ---------------------------------------------------------------------------
|
|
199
173
|
// Build the launch spec from {kind, mode}
|
|
200
174
|
// ---------------------------------------------------------------------------
|
|
201
|
-
/**
|
|
202
|
-
*
|
|
203
|
-
* (
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
175
|
+
/** The minimal static system prompt every node launches with — a thin launcher
|
|
176
|
+
* frame keeping the `\n\nGuidelines:` anchor the `before_agent_start` substrate
|
|
177
|
+
* hook (canvas-doc-substrate.ts) splices its `<memory kind="preference">` block
|
|
178
|
+
* before. ALL runtime/spine/lifecycle/kind prompting now lives as gated builtin
|
|
179
|
+
* memory docs (src/builtin-memory/), spliced in fresh every turn — this scaffold
|
|
180
|
+
* must never duplicate that prose. Exported so `sys prompt-review` can render
|
|
181
|
+
* through the exact same static+spliced pipeline boot uses, so the two can
|
|
182
|
+
* never drift. */
|
|
183
|
+
export const LAUNCH_SYSTEM_PROMPT = 'You are a node in the crtr canvas — an agentic runtime.\n\nGuidelines:\n' +
|
|
184
|
+
'- Use the crtr canvas and memory substrate according to injected context.';
|
|
185
|
+
/** Compose a node's full pi launch recipe from the kind registry. The system
|
|
186
|
+
* prompt is now a minimal static scaffold (`LAUNCH_SYSTEM_PROMPT`) — runtime/
|
|
187
|
+
* spine/lifecycle/kind prose is spliced per-turn from gated builtin memory docs,
|
|
188
|
+
* not baked in here. Launch KNOBS (model/tools/extensions) still come from the
|
|
189
|
+
* kind registry (`resolveKindConfig`), keyed on the FULL kind string (top-level
|
|
190
|
+
* or sub-kind). Model precedence, strongest to weakest:
|
|
191
|
+
* 1. Caller-supplied `opts.model` (durable on `meta.model_override`, re-passed
|
|
192
|
+
* on every polymorph).
|
|
193
|
+
* 2. The kind's registered model — mode-aware: orchestrator mode prefers
|
|
194
|
+
* `orchestratorModel`, falling back to `model`; base mode always uses
|
|
195
|
+
* `model`.
|
|
196
|
+
* 3. Unset → pi default.
|
|
197
|
+
* Callers pass the authoritative lifecycle + hasManager (`parent !== null`) so
|
|
198
|
+
* a polymorph/flip rebuilds the recipe faithfully; `lifecycle` is returned
|
|
199
|
+
* as-given — it is no longer persona-frontmatter-derived. The two canvas
|
|
200
|
+
* extensions are always first; kind-declared extensions follow. */
|
|
208
201
|
export function buildLaunchSpec(kind, mode, opts) {
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
// 1. Caller-supplied override (durable on `meta.model_override`, re-passed on
|
|
213
|
-
// every polymorph).
|
|
214
|
-
// 2. Configured persona strength for this kind.
|
|
215
|
-
// 3. The persona file's own frontmatter model.
|
|
216
|
-
// 4. Unset → pi default.
|
|
217
|
-
const chosenModel = opts.model ?? cfg.personaStrengths[kind] ?? p.model;
|
|
202
|
+
const kindConfig = resolveKindConfig(kind);
|
|
203
|
+
const kindModel = mode === 'orchestrator' ? kindConfig?.orchestratorModel ?? kindConfig?.model : kindConfig?.model;
|
|
204
|
+
const chosenModel = opts.model ?? kindModel;
|
|
218
205
|
const launch = {
|
|
219
206
|
model: chosenModel !== undefined ? normalizeModel(chosenModel) : undefined,
|
|
220
|
-
tools:
|
|
221
|
-
extensions: [...CANVAS_EXTENSIONS, ...
|
|
222
|
-
systemPrompt:
|
|
207
|
+
tools: kindConfig?.tools ?? [],
|
|
208
|
+
extensions: [...CANVAS_EXTENSIONS, ...(kindConfig?.extensions ?? [])],
|
|
209
|
+
systemPrompt: LAUNCH_SYSTEM_PROMPT,
|
|
223
210
|
env: { ...(opts.extraEnv ?? {}) },
|
|
224
211
|
};
|
|
225
|
-
return { launch, lifecycle:
|
|
212
|
+
return { launch, lifecycle: opts.lifecycle };
|
|
226
213
|
}
|
|
227
214
|
// ---------------------------------------------------------------------------
|
|
228
215
|
// Build the pi argv to launch / revive a node
|
|
@@ -47,6 +47,13 @@ export interface SpawnNodeOpts {
|
|
|
47
47
|
* persona default. Persisted to `meta.model_override` so polymorphs preserve
|
|
48
48
|
* it. Omit to use the persona default. */
|
|
49
49
|
modelOverride?: string | null;
|
|
50
|
+
/** The profile-directory id (`<slug>-<id>`) this node runs under; null for a
|
|
51
|
+
* root/no-profile node. Written to `meta.profile_id` and the row, and mirrored
|
|
52
|
+
* into the pi process env as `CRTR_PROFILE_ID` by `nodeEnv` — the runtime
|
|
53
|
+
* → resolver seam every downstream memory/gate consumer reads. Callers
|
|
54
|
+
* resolve/validate the operand (via `loadProfileManifest`) before it reaches
|
|
55
|
+
* here; this layer just persists the already-resolved id. */
|
|
56
|
+
profile_id?: string | null;
|
|
50
57
|
/** Override the generated id (e.g. when a caller pre-allocates one). */
|
|
51
58
|
nodeId?: string;
|
|
52
59
|
}
|
|
@@ -64,6 +64,11 @@ export function nodeEnv(meta) {
|
|
|
64
64
|
// resolves into the REPO, not here — agents must address artifacts via this.
|
|
65
65
|
CRTR_CONTEXT_DIR: contextDir(meta.node_id),
|
|
66
66
|
CRTR_CYCLES: (meta.cycles ?? 0).toString(),
|
|
67
|
+
// The runtime→resolver seam: memory-resolver.ts/scope.ts/config.ts read
|
|
68
|
+
// this process-wide env var (Phase 5) to pull in the node's profile memory
|
|
69
|
+
// store and multi-project pointer set. '' (never omitted) for a root/
|
|
70
|
+
// no-profile node, matching every other CRTR_* scalar here.
|
|
71
|
+
CRTR_PROFILE_ID: meta.profile_id ?? '',
|
|
67
72
|
};
|
|
68
73
|
if (meta.parent)
|
|
69
74
|
env['CRTR_PARENT_NODE_ID'] = meta.parent;
|
|
@@ -105,6 +110,7 @@ export function spawnNode(opts) {
|
|
|
105
110
|
// Broker is the only host: every node runs on the headless broker; a tmux
|
|
106
111
|
// pane is only an attach viewer.
|
|
107
112
|
host_kind: 'broker',
|
|
113
|
+
profile_id: opts.profile_id ?? null,
|
|
108
114
|
kind: opts.kind,
|
|
109
115
|
mode,
|
|
110
116
|
lifecycle,
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
// worker never gets spurious guidance). `personaDrift` compares live meta to it;
|
|
21
21
|
// the caller delivers the guidance, then commits the ack.
|
|
22
22
|
import { getNode, updateNode } from '../canvas/index.js';
|
|
23
|
-
import { loadKernel, loadPersona, loadLifecycleFragment } from '../personas/index.js';
|
|
24
23
|
import { resolveMemoryDoc } from '../memory-resolver.js';
|
|
25
24
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
26
25
|
import { orchestratorContextNote } from './bearings.js';
|
|
@@ -28,40 +27,30 @@ import { orchestratorContextNote } from './bearings.js';
|
|
|
28
27
|
// base→orchestrator guidance (the roadmap-shaping dump) — MOVED here from
|
|
29
28
|
// promote.ts so the injector is the one place that builds it.
|
|
30
29
|
// ---------------------------------------------------------------------------
|
|
31
|
-
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return v;
|
|
38
|
-
if (typeof v === 'number' || typeof v === 'boolean')
|
|
39
|
-
return String(v);
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
/** Load a knowledge doc's body text by name, or null if it can't be resolved.
|
|
43
|
-
* Used to inline a kind's roadmap-shaping knowledge doc into the orchestration
|
|
44
|
-
* guidance. */
|
|
45
|
-
function loadRoadmapKnowledgeBody(name) {
|
|
30
|
+
/** Load a builtin/user/project memory doc's body by its normalized name, or ''
|
|
31
|
+
* if it can't be resolved. The single source for the lifecycle/spine/
|
|
32
|
+
* orchestration-kernel fragments this injector re-delivers on a transition —
|
|
33
|
+
* the SAME docs Phase 2 authored under `src/builtin-memory/`, so static
|
|
34
|
+
* (system-prompt splice) and transition prose can never drift. */
|
|
35
|
+
function loadMemoryBody(name) {
|
|
46
36
|
try {
|
|
47
37
|
return resolveMemoryDoc(name).body.trim();
|
|
48
38
|
}
|
|
49
39
|
catch {
|
|
50
|
-
return
|
|
40
|
+
return '';
|
|
51
41
|
}
|
|
52
42
|
}
|
|
53
43
|
/** The base→orchestrator guidance dump, specialized to the node's kind: the
|
|
54
|
-
* shared kernel +
|
|
55
|
-
* the
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
44
|
+
* shared orchestration-kernel doc + the roadmap scaffold the node must author +
|
|
45
|
+
* the orchestrator context-dir framing + a short pointer at the substrate
|
|
46
|
+
* memory flow (promotion re-informs of nothing about the stores, it only adds
|
|
47
|
+
* the across-cycles relevance). The node is now a delegator whose scarce
|
|
48
|
+
* resource is its own context window. Roadmap-shaping guidance is just another orchestrator-gated memory
|
|
49
|
+
* doc the node already gets via the boot-render splice (kinds/<kind>/orchestrator
|
|
50
|
+
* gated `{kind, mode: orchestrator}`). (Lifecycle is left to its own section —
|
|
51
|
+
* promotion no longer forces resident, so this never asserts residency.) */
|
|
60
52
|
function orchestrationGuidance(nodeId, kind) {
|
|
61
|
-
const kernel =
|
|
62
|
-
const orch = loadPersona(kind, 'orchestrator');
|
|
63
|
-
const roadmapKnowledge = scalarToString(orch?.frontmatter?.['roadmapKnowledge']) ?? undefined;
|
|
64
|
-
const roadmapKnowledgeBody = roadmapKnowledge ? loadRoadmapKnowledgeBody(roadmapKnowledge) : null;
|
|
53
|
+
const kernel = loadMemoryBody('orchestration-kernel');
|
|
65
54
|
const roadmap = readRoadmap(nodeId) ?? '(no roadmap yet)';
|
|
66
55
|
const rmPath = roadmapPath(nodeId);
|
|
67
56
|
const parts = [
|
|
@@ -70,9 +59,6 @@ function orchestrationGuidance(nodeId, kind) {
|
|
|
70
59
|
'',
|
|
71
60
|
kernel,
|
|
72
61
|
];
|
|
73
|
-
if (roadmapKnowledgeBody) {
|
|
74
|
-
parts.push('', `--- How to shape a ${kind} roadmap (knowledge: ${roadmapKnowledge}) ---`, '', roadmapKnowledgeBody);
|
|
75
|
-
}
|
|
76
62
|
parts.push('', `Your roadmap scaffold (\`${rmPath}\`) — author it now: state the goal, exit criteria, and the phase skeleton, using the approach above. Current contents:`, '', roadmap, '',
|
|
77
63
|
// The orchestrator framing for the context dir — the missing guidance a
|
|
78
64
|
// promoted node never got at spawn (it spawned as a base worker). Same note
|
|
@@ -96,11 +82,11 @@ function baseModeGuidance() {
|
|
|
96
82
|
// only re-delivers that fragment as the node's new-state steer.
|
|
97
83
|
/** terminal → resident: interactable, never forced to submit. */
|
|
98
84
|
function residentLifecycleGuidance() {
|
|
99
|
-
return
|
|
85
|
+
return loadMemoryBody('lifecycle/resident');
|
|
100
86
|
}
|
|
101
87
|
/** resident → terminal: owes a final, reaps when done. */
|
|
102
88
|
function terminalLifecycleGuidance() {
|
|
103
|
-
return
|
|
89
|
+
return loadMemoryBody('lifecycle/terminal');
|
|
104
90
|
}
|
|
105
91
|
/** Build the injected transition prompt for a `from → to` persona change.
|
|
106
92
|
* Concatenates the relevant section per changed axis (both when both changed).
|
|
@@ -6,6 +6,11 @@ export interface BootRootOpts {
|
|
|
6
6
|
name?: string;
|
|
7
7
|
/** Optional starter prompt (bare `crtr` requires none). */
|
|
8
8
|
prompt?: string;
|
|
9
|
+
/** Explicit `--profile <id-or-name>` on the front-door invocation, validated
|
|
10
|
+
* through `loadProfileManifest`. Omit to run the MRU/create-or-root startup
|
|
11
|
+
* selector for `cwd` (see `selectProfileForCwd`) — a root has no spawner to
|
|
12
|
+
* inherit from, so this is the front door's only source of profile identity. */
|
|
13
|
+
profile?: string | null;
|
|
9
14
|
}
|
|
10
15
|
/** Create the front-door root: launch its broker engine, then exec `crtr surface attach`
|
|
11
16
|
* inline so THIS terminal becomes the broker's controller-viewer. Does not
|
|
@@ -41,6 +46,14 @@ export interface SpawnChildOpts {
|
|
|
41
46
|
* persona's declared default. Persisted to `meta.model_override` so it
|
|
42
47
|
* survives polymorphs. Omit to use the persona default. */
|
|
43
48
|
model?: string;
|
|
49
|
+
/** Select the profile this node runs under — an exact profile id or a unique
|
|
50
|
+
* manifest name, validated through `loadProfileManifest`. Omit to INHERIT the
|
|
51
|
+
* spawner's current `profile_id` (managed child or --root alike — --root only
|
|
52
|
+
* means top-level, it does not reset to no profile). Passed through unchanged
|
|
53
|
+
* into a deferred trigger recipe so a triggered birth re-resolves the SAME
|
|
54
|
+
* choice at fire time (an explicit value re-validates; inheritance re-reads
|
|
55
|
+
* the spawner's THEN-current profile). */
|
|
56
|
+
profile?: string | null;
|
|
44
57
|
}
|
|
45
58
|
/** Resolve a `--fork-from` value to an ABSOLUTE `.jsonl` source path for the
|
|
46
59
|
* broker fork (`SessionManager.forkFrom`, which loads a file — never a bare id).
|
|
@@ -14,6 +14,8 @@ import { readdirSync, existsSync } from 'node:fs';
|
|
|
14
14
|
import { isAbsolute, resolve, join } from 'node:path';
|
|
15
15
|
import { homedir } from 'node:os';
|
|
16
16
|
import { spawnNode, currentNodeContext, rootOfSpine } from './nodes.js';
|
|
17
|
+
import { loadProfileManifest } from '../profiles/manifest.js';
|
|
18
|
+
import { selectProfileForCwd } from '../profiles/select.js';
|
|
17
19
|
import { buildLaunchSpec, buildPiArgv } from './launch.js';
|
|
18
20
|
import { writeGoal } from './kickoff.js';
|
|
19
21
|
import { hasRoadmap, seedRoadmap } from './roadmap.js';
|
|
@@ -61,6 +63,10 @@ export async function bootRoot(opts) {
|
|
|
61
63
|
}
|
|
62
64
|
catch { /* daemon is best-effort */ }
|
|
63
65
|
const kind = opts.kind ?? 'general';
|
|
66
|
+
// The front door's only source of profile identity — a root has no spawner
|
|
67
|
+
// to inherit from. Runs BEFORE spawnNode: explicit --profile > MRU profile
|
|
68
|
+
// covering cwd > a synchronous create-or-root prompt (or root, headless).
|
|
69
|
+
const profileId = await selectProfileForCwd(opts.cwd, opts.profile);
|
|
64
70
|
// A born-resident root starts in base mode; it earns the orchestrator persona
|
|
65
71
|
// the first time it delegates (or on promotion). Resident lifecycle either way.
|
|
66
72
|
const { launch } = buildLaunchSpec(kind, 'base', { lifecycle: 'resident', hasManager: false });
|
|
@@ -75,6 +81,7 @@ export async function bootRoot(opts) {
|
|
|
75
81
|
cwd: opts.cwd,
|
|
76
82
|
name: opts.name ?? kind,
|
|
77
83
|
parent: null,
|
|
84
|
+
profile_id: profileId,
|
|
78
85
|
launch,
|
|
79
86
|
});
|
|
80
87
|
// Persist the spawning prompt as the goal so a fresh revive can re-read its
|
|
@@ -216,6 +223,22 @@ export function resolveForkSource(value) {
|
|
|
216
223
|
// .jsonl (the broker fork won't resolve a uuid).
|
|
217
224
|
return resolveSessionUuid(v);
|
|
218
225
|
}
|
|
226
|
+
/** Resolve the profile a spawned child runs under: an explicit `--profile`
|
|
227
|
+
* operand (id or name) validated through `loadProfileManifest`, else INHERIT
|
|
228
|
+
* the spawner's current `profile_id` (null when the spawner has none). `--root`
|
|
229
|
+
* never resets this to null on its own; only an explicit override does. When
|
|
230
|
+
* there is NO spawner at all — `crtr node new --root` run directly from a
|
|
231
|
+
* shell, not from inside a node — there is no profile to inherit, so this runs
|
|
232
|
+
* the same startup selector the front door uses (MRU covering `cwd`, else the
|
|
233
|
+
* synchronous create-or-root prompt / headless root default). */
|
|
234
|
+
async function resolveProfileId(explicit, spawner, cwd) {
|
|
235
|
+
if (explicit !== undefined && explicit !== null && explicit !== '') {
|
|
236
|
+
return loadProfileManifest(explicit).profileId;
|
|
237
|
+
}
|
|
238
|
+
if (spawner !== null)
|
|
239
|
+
return getNode(spawner)?.profile_id ?? null;
|
|
240
|
+
return selectProfileForCwd(cwd);
|
|
241
|
+
}
|
|
219
242
|
/** Resolve who a spawn is attributed to. A managed child needs a spine parent
|
|
220
243
|
* (explicit `--parent` or the calling node's CRTR_NODE_ID). A --root spawn
|
|
221
244
|
* does not: it is top-level by definition and the spawner identity is
|
|
@@ -256,6 +279,7 @@ export async function spawnChild(opts) {
|
|
|
256
279
|
// Born WITHOUT a name — the canvas-goal-capture extension names it async from
|
|
257
280
|
// its first message (the kickoff task) inside its own pi process, so spawn
|
|
258
281
|
// never blocks on the LLM naming round-trip (the 2-3s freeze it used to cost).
|
|
282
|
+
const profileId = await resolveProfileId(opts.profile, spawner, opts.cwd);
|
|
259
283
|
const meta = spawnNode({
|
|
260
284
|
kind: opts.kind,
|
|
261
285
|
mode,
|
|
@@ -267,6 +291,7 @@ export async function spawnChild(opts) {
|
|
|
267
291
|
// A child's parent IS its manager.
|
|
268
292
|
parent: root ? null : spawner,
|
|
269
293
|
spawnedBy: root ? (spawner ?? undefined) : undefined,
|
|
294
|
+
profile_id: profileId,
|
|
270
295
|
// Persist the RAW fork reference (not the resolved path) as provenance, so
|
|
271
296
|
// the boot intro can detect this is a fork and re-assert the node's own
|
|
272
297
|
// identity over the source's copied-in conversation.
|
package/dist/core/scope.d.ts
CHANGED
|
@@ -5,14 +5,21 @@ export declare function builtinPiPackagesDir(): string;
|
|
|
5
5
|
export declare function builtinPiPackageDir(name: string): string;
|
|
6
6
|
export declare function userScopeRoot(): string;
|
|
7
7
|
export declare function findProjectScopeRoot(startDir?: string): string | null;
|
|
8
|
-
/** Every project scope root visible from `startDir`, nearest first
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
8
|
+
/** Every project scope root visible from `startDir`, nearest first — now a
|
|
9
|
+
* PROFILE-AWARE pointer walk, not a single-pointer ancestor walk. Pointers are
|
|
10
|
+
* `[startDir]` plus, when `profileId` resolves to a manifest, each of its
|
|
11
|
+
* `projects` in manifest order; each pointer independently walks upward
|
|
12
|
+
* collecting every ancestor `.crouter/` dir, the pointers' contributions
|
|
13
|
+
* concatenate in pointer order, and the combined list dedupes to first
|
|
14
|
+
* occurrence — so a `.crouter/` shared by two pointers contributes once, at
|
|
15
|
+
* whichever pointer reached it first. `profileId` defaults to the process's
|
|
16
|
+
* selected profile (`CRTR_PROFILE_ID`); a null/unresolvable profile collapses
|
|
17
|
+
* to the single `[startDir]` pointer, reproducing today's behavior exactly.
|
|
18
|
+
* Results are cached per (resolved startDir, profileId) — `resetScopeCache()`
|
|
19
|
+
* invalidates on any profile mutation that can change project dirs or names. */
|
|
20
|
+
export declare function findProjectScopeRoots(startDir?: string, profileId?: string | null): string[];
|
|
14
21
|
export declare function projectScopeRoot(startDir?: string): string | null;
|
|
15
|
-
export declare function projectScopeRoots(startDir?: string): string[];
|
|
22
|
+
export declare function projectScopeRoots(startDir?: string, profileId?: string | null): string[];
|
|
16
23
|
export declare function scopeRoot(scope: Scope): string | null;
|
|
17
24
|
export declare function requireScopeRoot(scope: Scope): string;
|
|
18
25
|
export declare function ensureProjectScopeRoot(startDir?: string): string;
|
package/dist/core/scope.js
CHANGED
|
@@ -4,7 +4,19 @@ import { join, resolve, dirname } from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { CRTR_DIR_NAME } from '../types.js';
|
|
6
6
|
import { usage } from './errors.js';
|
|
7
|
-
|
|
7
|
+
import { loadProfileManifest } from './profiles/manifest.js';
|
|
8
|
+
// Cache of resolved project-scope-root STACKS, keyed by (resolved cwd, profile
|
|
9
|
+
// id). A profile widens the pointer set beyond the bare cwd (see
|
|
10
|
+
// findProjectScopeRoots below), so the cache key must carry both — the same
|
|
11
|
+
// cwd resolves to a different stack under a different (or no) profile.
|
|
12
|
+
// `resetScopeCache()` (called by every profile mutation in
|
|
13
|
+
// src/core/profiles/manifest.ts) clears the whole cache; there is no
|
|
14
|
+
// per-entry invalidation because a profile edit can change which pointer set
|
|
15
|
+
// any cached cwd belongs to.
|
|
16
|
+
const scopeRootsCache = new Map();
|
|
17
|
+
function scopeRootsCacheKey(resolvedStartDir, profileId) {
|
|
18
|
+
return `${resolvedStartDir}\u0000${profileId ?? ''}`;
|
|
19
|
+
}
|
|
8
20
|
export function builtinViewsRoot() {
|
|
9
21
|
// Resolved relative to this file: dist/core/scope.js → dist/builtin-views/
|
|
10
22
|
// (src/ at dev time).
|
|
@@ -47,20 +59,13 @@ function isProjectScopeDir(candidate, userRoot) {
|
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
61
|
export function findProjectScopeRoot(startDir = process.cwd()) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
/** Every project scope root visible from `startDir`, nearest first. Each
|
|
57
|
-
* ancestor directory with a `.crouter/` contributes memory/preferences to the
|
|
58
|
-
* current workspace; the nearest root still wins for writes and duplicate doc
|
|
59
|
-
* names. The user-global `~/.crouter/` is excluded because it is the separate
|
|
60
|
-
* user scope, not a project ancestor. */
|
|
61
|
-
export function findProjectScopeRoots(startDir = process.cwd()) {
|
|
62
|
+
return findProjectScopeRoots(startDir)[0] ?? null;
|
|
63
|
+
}
|
|
64
|
+
/** Walk `startDir` upward to the filesystem root, collecting every ancestor
|
|
65
|
+
* `.crouter/` dir (nearest first). The user-global `~/.crouter/` is excluded
|
|
66
|
+
* because it is the separate user scope, not a project ancestor. */
|
|
67
|
+
function collectAncestorScopeRoots(startDir, userRoot) {
|
|
62
68
|
const roots = [];
|
|
63
|
-
const userRoot = userScopeRoot();
|
|
64
69
|
let dir = resolve(startDir);
|
|
65
70
|
while (true) {
|
|
66
71
|
const candidate = join(dir, CRTR_DIR_NAME);
|
|
@@ -72,11 +77,55 @@ export function findProjectScopeRoots(startDir = process.cwd()) {
|
|
|
72
77
|
dir = parent;
|
|
73
78
|
}
|
|
74
79
|
}
|
|
80
|
+
/** Every project scope root visible from `startDir`, nearest first — now a
|
|
81
|
+
* PROFILE-AWARE pointer walk, not a single-pointer ancestor walk. Pointers are
|
|
82
|
+
* `[startDir]` plus, when `profileId` resolves to a manifest, each of its
|
|
83
|
+
* `projects` in manifest order; each pointer independently walks upward
|
|
84
|
+
* collecting every ancestor `.crouter/` dir, the pointers' contributions
|
|
85
|
+
* concatenate in pointer order, and the combined list dedupes to first
|
|
86
|
+
* occurrence — so a `.crouter/` shared by two pointers contributes once, at
|
|
87
|
+
* whichever pointer reached it first. `profileId` defaults to the process's
|
|
88
|
+
* selected profile (`CRTR_PROFILE_ID`); a null/unresolvable profile collapses
|
|
89
|
+
* to the single `[startDir]` pointer, reproducing today's behavior exactly.
|
|
90
|
+
* Results are cached per (resolved startDir, profileId) — `resetScopeCache()`
|
|
91
|
+
* invalidates on any profile mutation that can change project dirs or names. */
|
|
92
|
+
export function findProjectScopeRoots(startDir = process.cwd(), profileId = process.env['CRTR_PROFILE_ID'] || null) {
|
|
93
|
+
const resolvedStart = resolve(startDir);
|
|
94
|
+
const key = scopeRootsCacheKey(resolvedStart, profileId);
|
|
95
|
+
const cached = scopeRootsCache.get(key);
|
|
96
|
+
if (cached !== undefined)
|
|
97
|
+
return cached;
|
|
98
|
+
const userRoot = userScopeRoot();
|
|
99
|
+
const pointers = [resolvedStart];
|
|
100
|
+
if (profileId !== null && profileId !== '') {
|
|
101
|
+
try {
|
|
102
|
+
const { manifest } = loadProfileManifest(profileId);
|
|
103
|
+
pointers.push(...manifest.projects);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Missing/deleted/invalid profile: this is a hot path every memory and
|
|
107
|
+
// launch-config resolve runs through, so fall back to the bare
|
|
108
|
+
// `[startDir]` pointer rather than throwing.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const roots = [];
|
|
113
|
+
for (const pointer of pointers) {
|
|
114
|
+
for (const root of collectAncestorScopeRoots(pointer, userRoot)) {
|
|
115
|
+
if (seen.has(root))
|
|
116
|
+
continue;
|
|
117
|
+
seen.add(root);
|
|
118
|
+
roots.push(root);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
scopeRootsCache.set(key, roots);
|
|
122
|
+
return roots;
|
|
123
|
+
}
|
|
75
124
|
export function projectScopeRoot(startDir) {
|
|
76
125
|
return findProjectScopeRoot(startDir);
|
|
77
126
|
}
|
|
78
|
-
export function projectScopeRoots(startDir) {
|
|
79
|
-
return findProjectScopeRoots(startDir);
|
|
127
|
+
export function projectScopeRoots(startDir, profileId) {
|
|
128
|
+
return findProjectScopeRoots(startDir, profileId);
|
|
80
129
|
}
|
|
81
130
|
export function scopeRoot(scope) {
|
|
82
131
|
// The builtin scope has no scope-root dir: its only content is memory
|
|
@@ -96,9 +145,16 @@ export function ensureProjectScopeRoot(startDir = process.cwd()) {
|
|
|
96
145
|
const found = findProjectScopeRoot(startDir);
|
|
97
146
|
if (found)
|
|
98
147
|
return found;
|
|
99
|
-
// Initialize new project scope at startDir
|
|
100
|
-
|
|
101
|
-
|
|
148
|
+
// Initialize new project scope at startDir. `found` was falsy, which means
|
|
149
|
+
// the (resolvedStart, profileId) cache key above resolved to an EMPTY stack
|
|
150
|
+
// (no pointer had an existing `.crouter/` yet) — seed that same cache entry
|
|
151
|
+
// with the about-to-exist root so a caller that immediately writes under it
|
|
152
|
+
// (without an intervening disk rescan) sees it without a stale empty-array
|
|
153
|
+
// cache hit.
|
|
154
|
+
const resolvedStart = resolve(startDir);
|
|
155
|
+
const root = join(resolvedStart, CRTR_DIR_NAME);
|
|
156
|
+
const profileId = process.env['CRTR_PROFILE_ID'] || null;
|
|
157
|
+
scopeRootsCache.set(scopeRootsCacheKey(resolvedStart, profileId), [root]);
|
|
102
158
|
return root;
|
|
103
159
|
}
|
|
104
160
|
export function pluginsDir(scope) {
|
|
@@ -154,5 +210,5 @@ export function listScopes(scopeArg) {
|
|
|
154
210
|
return [v];
|
|
155
211
|
}
|
|
156
212
|
export function resetScopeCache() {
|
|
157
|
-
|
|
213
|
+
scopeRootsCache.clear();
|
|
158
214
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { KINDS, isDocKind, RUNGS, rungRank, rungAtLeast, FALLBACK_RUNG, parseSubstrateFrontmatter, parseSubstrateDoc, previewLine, } from './schema.js';
|
|
1
|
+
export { KINDS, isDocKind, RUNGS, rungRank, rungAtLeast, FALLBACK_RUNG, parseSubstrateFrontmatter, parseSubstrateDoc, previewLine, normalizeNameSegment, normalizeDocName, } from './schema.js';
|
|
2
2
|
export type { DocKind, Rung, GatePredicate, SubstrateSchema, SubstrateDoc } from './schema.js';
|
|
3
|
-
export { scopeForCwd, spineDepth, assembleNodeSubject } from './subject.js';
|
|
3
|
+
export { scopeForCwd, spineDepth, assembleNodeSubject, profileNameFor } from './subject.js';
|
|
4
4
|
export type { NodeConfigSubject } from './subject.js';
|
|
5
5
|
export { gatePasses } from './gate.js';
|
|
6
6
|
export { INDEX_NAME, isIndexName, indexDirOf, displayName, buildCeilingIndex, effectiveRung, } from './ceiling.js';
|
|
7
7
|
export type { Surface } from './ceiling.js';
|
|
8
|
-
export { renderPreferencesSection, renderKnowledgeBlock, } from './render.js';
|
|
8
|
+
export { renderPreferencesSection, renderKnowledgeBlock, renderPreferencesForSubject, } from './render.js';
|
|
9
9
|
export { renderOnReadDocs } from './on-read.js';
|
|
@@ -11,9 +11,11 @@ RUNGS, rungRank, rungAtLeast,
|
|
|
11
11
|
// fallback floor
|
|
12
12
|
FALLBACK_RUNG,
|
|
13
13
|
// parse + render-shared helpers
|
|
14
|
-
parseSubstrateFrontmatter, parseSubstrateDoc, previewLine,
|
|
15
|
-
|
|
14
|
+
parseSubstrateFrontmatter, parseSubstrateDoc, previewLine,
|
|
15
|
+
// display-name normalization
|
|
16
|
+
normalizeNameSegment, normalizeDocName, } from './schema.js';
|
|
17
|
+
export { scopeForCwd, spineDepth, assembleNodeSubject, profileNameFor } from './subject.js';
|
|
16
18
|
export { gatePasses } from './gate.js';
|
|
17
19
|
export { INDEX_NAME, isIndexName, indexDirOf, displayName, buildCeilingIndex, effectiveRung, } from './ceiling.js';
|
|
18
|
-
export { renderPreferencesSection, renderKnowledgeBlock, } from './render.js';
|
|
20
|
+
export { renderPreferencesSection, renderKnowledgeBlock, renderPreferencesForSubject, } from './render.js';
|
|
19
21
|
export { renderOnReadDocs } from './on-read.js';
|
|
@@ -56,7 +56,7 @@ import { pathExists, readText, walkFiles } from '../fs-utils.js';
|
|
|
56
56
|
import { parseFrontmatterGeneric } from '../frontmatter.js';
|
|
57
57
|
import { evalCondition } from '../predicate.js';
|
|
58
58
|
import { listAllMemoryDocs } from '../memory-resolver.js';
|
|
59
|
-
import { assembleNodeSubject, buildCeilingIndex, displayName, effectiveRung, gatePasses, parseSubstrateDoc, parseSubstrateFrontmatter, previewLine, } from './index.js';
|
|
59
|
+
import { assembleNodeSubject, buildCeilingIndex, displayName, effectiveRung, gatePasses, normalizeDocName, parseSubstrateDoc, parseSubstrateFrontmatter, previewLine, } from './index.js';
|
|
60
60
|
import { cachedSubstrateDocs } from './session-cache.js';
|
|
61
61
|
// Ancestor dirs we never look inside for a `.crouter/memory/` store (the read
|
|
62
62
|
// file may live under a build/dependency tree; `.crouter` is NOT junk here — it
|
|
@@ -113,10 +113,11 @@ function isJunkAncestor(dir) {
|
|
|
113
113
|
* (gate eval keys off the NODE subject, render off name/body/rung). */
|
|
114
114
|
function loadPositionalDoc(file, memDir, scope) {
|
|
115
115
|
try {
|
|
116
|
-
const
|
|
116
|
+
const raw = relative(memDir, file)
|
|
117
117
|
.replace(/\.md$/i, '')
|
|
118
118
|
.split(sep)
|
|
119
119
|
.join('/');
|
|
120
|
+
const name = normalizeDocName(raw);
|
|
120
121
|
if (name === '')
|
|
121
122
|
return null;
|
|
122
123
|
const { data, body } = parseFrontmatterGeneric(readText(file));
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
* then (when any are eligible) the `kind: preference` docs in one tree at their
|
|
3
|
-
* effective rung (the preference default rung is `preview` → the routing line).
|
|
4
|
-
* ALWAYS returns a non-empty `<memory kind="preference">` block — the guidance
|
|
5
|
-
* frame is unconditional, so the system-prompt splice is never empty even when
|
|
6
|
-
* no preference is eligible. */
|
|
1
|
+
import { type NodeConfigSubject, type SubstrateDoc } from './index.js';
|
|
7
2
|
export declare function renderPreferencesSection(nodeId: string): string;
|
|
3
|
+
export declare function renderPreferencesForSubject(subject: NodeConfigSubject): {
|
|
4
|
+
block: string;
|
|
5
|
+
contentDocs: SubstrateDoc[];
|
|
6
|
+
tree: string;
|
|
7
|
+
};
|
|
8
8
|
/** The `<memory kind="knowledge">` block embedded in the session_start bearings
|
|
9
9
|
* message (bearings.ts pushes it as a SIBLING of `<crtr-bearings>`, or drops it
|
|
10
10
|
* when ''). The consultable catalog: every eligible `kind: knowledge` resolver
|
|
11
|
-
* doc
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* doc PLUS the node-local memory docs (any kind) — resolver docs win
|
|
12
|
+
* first-wins dedup over a same-named node-local doc. GROUPED by rung exactly
|
|
13
|
+
* like the preference block: content prose first (general→specific,
|
|
14
|
+
* node-local last), then the preview/name catalog tree. Node-local docs are
|
|
15
|
+
* floored HERE — a rung-none node-local doc's effective rung is bumped to
|
|
16
|
+
* `name` before grouping, so it still surfaces as a bare-name tree entry
|
|
17
|
+
* rather than collapsing into a `[+N more]` count (its body still never
|
|
18
|
+
* renders: floored docs never reach the `content` rung). Procedural guidance
|
|
19
|
+
* and factual references both live here as `knowledge`. Returns '' when
|
|
20
|
+
* nothing is eligible. */
|
|
16
21
|
export declare function renderKnowledgeBlock(nodeId: string): string;
|