@crouton-kit/crouter 0.3.36 → 0.3.38
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 +3 -6
- package/dist/{builtin-personas/runtime-base.md → builtin-memory/00-runtime-base.md} +8 -1
- 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-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +4 -4
- package/dist/builtin-views/canvas/core.mjs +3 -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/__tests__/crtr-output-render.test.js +21 -205
- package/dist/clients/attach/attach-cmd.js +429 -432
- package/dist/clients/web/web-cmd.js +7 -7
- package/dist/commands/__tests__/human.test.js +1 -15
- package/dist/commands/attention.js +9 -6
- package/dist/commands/canvas-history/search.js +2 -2
- package/dist/commands/canvas-history/show.js +3 -3
- package/dist/commands/canvas-history.js +1 -1
- package/dist/commands/canvas-issue.js +1 -0
- package/dist/commands/canvas-snapshot.js +1 -0
- package/dist/commands/canvas.js +10 -11
- package/dist/commands/chord.js +1 -0
- package/dist/commands/human/prompts.js +0 -1
- package/dist/commands/human/queue.js +1 -0
- 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-snapshot.js +1 -0
- package/dist/commands/node.js +52 -22
- 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/push.d.ts +0 -1
- package/dist/commands/push.js +2 -228
- package/dist/commands/revive.js +2 -2
- package/dist/commands/{canvas-tmux-spread.js → surface-tmux-spread.js} +9 -6
- package/dist/commands/surface.js +3 -2
- package/dist/commands/sys/config.js +6 -21
- package/dist/commands/sys/daemon.d.ts +2 -0
- package/dist/commands/{daemon.js → sys/daemon.js} +12 -12
- package/dist/commands/sys/doctor.js +1 -0
- package/dist/commands/sys/prompt-review.js +90 -17
- package/dist/commands/sys.js +4 -3
- package/dist/core/__tests__/daemon-boot.test.js +1 -1
- package/dist/core/__tests__/daemon-wedge.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-wedge.test.js +87 -0
- package/dist/core/__tests__/hearth-bootstrap.test.js +2 -2
- package/dist/core/__tests__/history-inbox.test.d.ts +1 -0
- package/dist/core/__tests__/history-inbox.test.js +105 -0
- package/dist/core/__tests__/kickoff.test.js +3 -3
- package/dist/core/__tests__/revive.test.js +39 -1
- package/dist/core/canvas/canvas.js +10 -9
- package/dist/core/canvas/db.js +11 -0
- package/dist/core/canvas/history.d.ts +1 -1
- package/dist/core/canvas/history.js +91 -3
- package/dist/core/canvas/paths.d.ts +6 -0
- package/dist/core/canvas/paths.js +9 -0
- package/dist/core/canvas/status-glyph.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/fault-classifier.d.ts +2 -2
- package/dist/core/feed/inbox.d.ts +7 -12
- package/dist/core/feed/inbox.js +45 -29
- package/dist/core/hearth/providers/blaxel-bootstrap.js +1 -1
- package/dist/core/memory-resolver.d.ts +26 -15
- package/dist/core/memory-resolver.js +158 -40
- package/dist/core/preview-registry.js +14 -112
- 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/render.js +1 -1
- package/dist/core/runtime/bearings.d.ts +9 -0
- package/dist/core/runtime/bearings.js +31 -0
- package/dist/core/runtime/busy.d.ts +15 -0
- package/dist/core/runtime/busy.js +32 -1
- package/dist/core/runtime/close.d.ts +10 -0
- package/dist/core/runtime/close.js +18 -13
- package/dist/core/runtime/fault-recovery.js +4 -0
- package/dist/core/runtime/front-door.js +9 -2
- package/dist/core/runtime/kickoff.js +5 -7
- 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/placement.js +1 -1
- package/dist/core/runtime/revive.js +9 -1
- package/dist/core/runtime/spawn.d.ts +13 -0
- package/dist/core/runtime/spawn.js +25 -0
- package/dist/core/runtime/tmux.d.ts +2 -2
- package/dist/core/runtime/tmux.js +2 -2
- 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/daemon/crtrd-cli.js +1 -1
- package/dist/daemon/crtrd.d.ts +24 -0
- package/dist/daemon/crtrd.js +213 -5
- package/dist/daemon/manage.js +2 -2
- package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
- package/dist/pi-extensions/canvas-nav.js +2 -2
- package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
- package/dist/pi-extensions/canvas-stophook.js +59 -2
- package/dist/types.d.ts +57 -1
- package/dist/types.js +83 -1
- package/dist/web-client/assets/{index-IAJVtuVe.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/commands/daemon.d.ts +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
- /package/dist/commands/{canvas-tmux-spread.d.ts → surface-tmux-spread.d.ts} +0 -0
|
@@ -58,6 +58,13 @@ export interface NodeIdentity {
|
|
|
58
58
|
* `cwd`, re-derived on index rebuild), NOT live runtime like `pane`.
|
|
59
59
|
* NULL ⇒ 'tmux' (every pre-broker node). */
|
|
60
60
|
host_kind?: 'tmux' | 'broker' | null;
|
|
61
|
+
/** The profile this node runs under, a stable profile-directory id
|
|
62
|
+
* (`<slug>-<id>`, see `core/profiles/manifest.ts`). Durable IDENTITY mirrored
|
|
63
|
+
* to the row (like `cwd`/`host_kind`), NOT re-derived by `rebuildIndex()` —
|
|
64
|
+
* it is chosen once at spawn (inherited from the spawner, or `--profile`) and
|
|
65
|
+
* is the runtime→resolver seam (`CRTR_PROFILE_ID`) the memory/gate substrate
|
|
66
|
+
* reads. NULL for a root/no-profile node — never invent a literal root id. */
|
|
67
|
+
profile_id?: string | null;
|
|
61
68
|
/** Role the node was born as: explore | developer | plan | review | general… */
|
|
62
69
|
kind: string;
|
|
63
70
|
mode: Mode;
|
|
@@ -161,6 +168,9 @@ export interface NodeRow {
|
|
|
161
168
|
cwd: string;
|
|
162
169
|
/** Which HOST launches + supervises this node; see NodeIdentity.host_kind. */
|
|
163
170
|
host_kind: 'tmux' | 'broker' | null;
|
|
171
|
+
/** The profile this node runs under; see NodeIdentity.profile_id. NULL for a
|
|
172
|
+
* root/no-profile node. */
|
|
173
|
+
profile_id: string | null;
|
|
164
174
|
parent: string | null;
|
|
165
175
|
created: string;
|
|
166
176
|
/** Authoritative runtime columns (see NodeRuntime). */
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Scope, ScopeConfig, ScopeState } from '../types.js';
|
|
1
|
+
import type { Scope, ScopeConfig, ScopeState, KindConfig, ModelLaddersConfig } from '../types.js';
|
|
2
2
|
export declare function configPath(scope: Scope): string | null;
|
|
3
3
|
export declare function statePath(scope: Scope): string | null;
|
|
4
4
|
export declare function readConfig(scope: Scope): ScopeConfig;
|
|
@@ -6,5 +6,48 @@ export declare function readState(scope: Scope): ScopeState;
|
|
|
6
6
|
export declare function writeConfig(scope: Scope, config: ScopeConfig): void;
|
|
7
7
|
export declare function writeState(scope: Scope, state: ScopeState): void;
|
|
8
8
|
export declare function ensureScopeInitialized(scope: Scope, root: string): void;
|
|
9
|
+
export interface MergedLaunchConfig {
|
|
10
|
+
kinds: Record<string, KindConfig>;
|
|
11
|
+
modelLadders: ModelLaddersConfig;
|
|
12
|
+
}
|
|
13
|
+
/** Merge launch knobs (`kinds`, `modelLadders`) across scopes in
|
|
14
|
+
* project stack > profile > user > builtin precedence — the same precedence
|
|
15
|
+
* order used for memory resolution. A kind or ladder cell declared at a
|
|
16
|
+
* more-specific scope shadows the same key from a less-specific scope; the
|
|
17
|
+
* project STACK (`findProjectScopeRoots` — every ancestor `.crouter/`,
|
|
18
|
+
* widened by a selected profile's `projects`) layers nearest-root-strongest;
|
|
19
|
+
* a key no scope declares falls through to the builtin default registry
|
|
20
|
+
* (`defaultScopeConfig().kinds` / `.modelLadders`).
|
|
21
|
+
*
|
|
22
|
+
* Layers RAW (un-defaulted) partial config per scope, not `readConfig(scope)`
|
|
23
|
+
* — `readConfig` already fills in every default kind for a scope that
|
|
24
|
+
* customizes even one, so naively overlaying two already-defaulted
|
|
25
|
+
* `ScopeConfig.kinds` objects would let an untouched project-scope kind
|
|
26
|
+
* (silently defaulted) clobber a real user-scope customization of that same
|
|
27
|
+
* kind. Layering the raw partials avoids that.
|
|
28
|
+
*
|
|
29
|
+
* Callers (launch, kind registry) go through this function rather than
|
|
30
|
+
* `readConfig` directly, so `ScopeConfig.kinds`/`modelLadders`/tools/
|
|
31
|
+
* extensions/`availableTo` all honor the same profile + multi-project
|
|
32
|
+
* precedence. */
|
|
33
|
+
export declare function readMergedLaunchConfig(): MergedLaunchConfig;
|
|
34
|
+
/** The effective `KindConfig` for one full kind string (top-level, e.g.
|
|
35
|
+
* `developer`, or sub-kind, e.g. `plan/reviewers/security`), across
|
|
36
|
+
* project > user > builtin precedence. Returns `undefined` for a kind no
|
|
37
|
+
* scope registers — existence is deliberately NOT validated here (kind
|
|
38
|
+
* existence/launch-menu enumeration is a Phase 3 caller concern); this only
|
|
39
|
+
* resolves the config for a kind the caller already knows about. */
|
|
40
|
+
export declare function resolveKindConfig(kind: string): KindConfig | undefined;
|
|
41
|
+
/** The sub-kinds available to spawn FROM a given top-level kind — every
|
|
42
|
+
* registered sub-kind (full path contains `/`) whose `availableTo` (default:
|
|
43
|
+
* its own top-level ancestor, e.g. `plan/reviewers/security` defaults to
|
|
44
|
+
* `['plan']`) includes `kind` or `'*'`. The single source both `sys
|
|
45
|
+
* prompt-review --list`'s `subPersonas` metadata and the live sub-persona
|
|
46
|
+
* spawn-menu splice (`core/substrate/render.ts`) read, so the two can never
|
|
47
|
+
* drift apart. Sorted by full kind name for stable rendering. */
|
|
48
|
+
export declare function subKindsAvailableTo(kind: string): {
|
|
49
|
+
kind: string;
|
|
50
|
+
whenToUse: string;
|
|
51
|
+
}[];
|
|
9
52
|
export declare function updateConfig(scope: Scope, mutate: (cfg: ScopeConfig) => void): ScopeConfig;
|
|
10
53
|
export declare function updateState(scope: Scope, mutate: (s: ScopeState) => void): ScopeState;
|
package/dist/core/config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { CONFIG_FILE, STATE_FILE, defaultScopeConfig, defaultScopeState, defaultCanvasNavConfig, defaultModelLaddersConfig } from '../types.js';
|
|
2
|
+
import { CONFIG_FILE, STATE_FILE, defaultScopeConfig, defaultScopeState, defaultCanvasNavConfig, defaultModelLaddersConfig, defaultKindsConfig } from '../types.js';
|
|
3
3
|
import { readJsonIfExists, writeJson, ensureDir } from './fs-utils.js';
|
|
4
|
-
import { scopeRoot, requireScopeRoot } from './scope.js';
|
|
4
|
+
import { scopeRoot, requireScopeRoot, findProjectScopeRoots } from './scope.js';
|
|
5
|
+
import { profileRoot } from './profiles/manifest.js';
|
|
5
6
|
function configPathFor(root) {
|
|
6
7
|
return join(root, CONFIG_FILE);
|
|
7
8
|
}
|
|
@@ -150,14 +151,15 @@ function mergeCanvasNav(raw) {
|
|
|
150
151
|
graphBinds: mergeBinds(defaults.graphBinds, r.graphBinds, normalizeGraphBind),
|
|
151
152
|
};
|
|
152
153
|
}
|
|
153
|
-
function mergeModelLadders(raw) {
|
|
154
|
-
const defaults =
|
|
154
|
+
function mergeModelLadders(raw, base = defaultModelLaddersConfig()) {
|
|
155
|
+
const defaults = base;
|
|
155
156
|
if (raw === null || typeof raw !== 'object')
|
|
156
157
|
return defaults;
|
|
157
158
|
const r = raw;
|
|
158
159
|
const out = {
|
|
159
160
|
anthropic: { ...defaults.anthropic },
|
|
160
161
|
openai: { ...defaults.openai },
|
|
162
|
+
...(defaults.defaultProvider !== undefined ? { defaultProvider: defaults.defaultProvider } : {}),
|
|
161
163
|
};
|
|
162
164
|
if (r.defaultProvider === 'anthropic' || r.defaultProvider === 'openai') {
|
|
163
165
|
out.defaultProvider = r.defaultProvider;
|
|
@@ -174,13 +176,42 @@ function mergeModelLadders(raw) {
|
|
|
174
176
|
}
|
|
175
177
|
return out;
|
|
176
178
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
/** Validate one raw `kinds.<name>` entry into a `KindConfig`, or drop it
|
|
180
|
+
* (return null) rather than throwing — an invalid kind entry in config.json
|
|
181
|
+
* must not break config parsing for every other kind. `whenToUse` is the
|
|
182
|
+
* only required field. */
|
|
183
|
+
function normalizeKindEntry(raw) {
|
|
179
184
|
if (raw === null || typeof raw !== 'object')
|
|
180
|
-
return
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
return null;
|
|
186
|
+
const r = raw;
|
|
187
|
+
if (typeof r.whenToUse !== 'string' || r.whenToUse.trim() === '')
|
|
188
|
+
return null;
|
|
189
|
+
const out = { whenToUse: r.whenToUse };
|
|
190
|
+
if (typeof r.model === 'string' && r.model.trim() !== '')
|
|
191
|
+
out.model = r.model;
|
|
192
|
+
if (typeof r.orchestratorModel === 'string' && r.orchestratorModel.trim() !== '')
|
|
193
|
+
out.orchestratorModel = r.orchestratorModel;
|
|
194
|
+
const isStringArray = (v) => Array.isArray(v) && v.every((item) => typeof item === 'string');
|
|
195
|
+
if (isStringArray(r.tools))
|
|
196
|
+
out.tools = r.tools;
|
|
197
|
+
if (isStringArray(r.extensions))
|
|
198
|
+
out.extensions = r.extensions;
|
|
199
|
+
if (isStringArray(r.availableTo))
|
|
200
|
+
out.availableTo = r.availableTo;
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
/** Merge a raw `kinds` block over `base` (defaulting to the builtin registry):
|
|
204
|
+
* each valid entry adds or shadows a kind by name; invalid entries are
|
|
205
|
+
* dropped, never thrown. Mirrors `mergeModelLadders`'s layer-over-defaults
|
|
206
|
+
* shape so a user/project config.json can add or override a single kind
|
|
207
|
+
* without restating the whole registry. */
|
|
208
|
+
function mergeKinds(raw, base = defaultKindsConfig()) {
|
|
209
|
+
const out = { ...base };
|
|
210
|
+
if (raw !== null && typeof raw === 'object') {
|
|
211
|
+
for (const [kind, value] of Object.entries(raw)) {
|
|
212
|
+
const normalized = normalizeKindEntry(value);
|
|
213
|
+
if (normalized !== null)
|
|
214
|
+
out[kind] = normalized;
|
|
184
215
|
}
|
|
185
216
|
}
|
|
186
217
|
return out;
|
|
@@ -215,8 +246,120 @@ function mergeConfig(partial) {
|
|
|
215
246
|
: defaults.max_panes_per_window;
|
|
216
247
|
const canvasNav = mergeCanvasNav(partial.canvasNav);
|
|
217
248
|
const modelLadders = mergeModelLadders(partial.modelLadders);
|
|
218
|
-
const
|
|
219
|
-
return { schema_version, marketplaces, plugins, auto_update, max_panes_per_window, canvasNav, modelLadders,
|
|
249
|
+
const kinds = mergeKinds(partial.kinds);
|
|
250
|
+
return { schema_version, marketplaces, plugins, auto_update, max_panes_per_window, canvasNav, modelLadders, kinds };
|
|
251
|
+
}
|
|
252
|
+
/** Raw (un-defaulted) partial config for one scope, or null if the scope has
|
|
253
|
+
* no root or no config.json. Used by `readMergedLaunchConfig` to layer
|
|
254
|
+
* scopes onto each other WITHOUT each scope's own default-fill masking a
|
|
255
|
+
* lower-precedence scope's real customization (see that function's comment
|
|
256
|
+
* for why `readConfig(scope)`, which already defaults-fills, is unusable
|
|
257
|
+
* for cross-scope layering). */
|
|
258
|
+
function readRawScopeConfig(scope) {
|
|
259
|
+
const root = scopeRoot(scope);
|
|
260
|
+
if (!root)
|
|
261
|
+
return null;
|
|
262
|
+
return readJsonIfExists(configPathFor(root));
|
|
263
|
+
}
|
|
264
|
+
/** Raw partial config at `<profileRoot(profileId)>/config.json`, or null when
|
|
265
|
+
* no profile is selected, the profile id no longer resolves (deleted/
|
|
266
|
+
* invalid), or it has no config.json. `profileRoot` centralizes the id-shape
|
|
267
|
+
* + containment checks (never a raw path join), and any resolution failure
|
|
268
|
+
* is swallowed here — launch config resolution is a hot path that must never
|
|
269
|
+
* throw on a stale `CRTR_PROFILE_ID`. */
|
|
270
|
+
function readRawProfileConfig(profileId) {
|
|
271
|
+
if (profileId === null || profileId === '')
|
|
272
|
+
return null;
|
|
273
|
+
try {
|
|
274
|
+
return readJsonIfExists(configPathFor(profileRoot(profileId)));
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/** Raw partial configs for the whole project-scope STACK (`findProjectScopeRoots`
|
|
281
|
+
* — every ancestor `.crouter/`, widened by a selected profile's `projects`),
|
|
282
|
+
* ordered FARTHEST-first so `readMergedLaunchConfig` can layer them on last
|
|
283
|
+
* and have the NEAREST root win — mirroring the resolver's nearest-project-
|
|
284
|
+
* strongest replacement precedence. */
|
|
285
|
+
function readRawProjectScopeConfigs() {
|
|
286
|
+
const nearestFirst = findProjectScopeRoots();
|
|
287
|
+
const farthestFirst = [...nearestFirst].reverse();
|
|
288
|
+
const out = [];
|
|
289
|
+
for (const root of farthestFirst) {
|
|
290
|
+
const raw = readJsonIfExists(configPathFor(root));
|
|
291
|
+
if (raw !== null)
|
|
292
|
+
out.push(raw);
|
|
293
|
+
}
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
296
|
+
/** Merge launch knobs (`kinds`, `modelLadders`) across scopes in
|
|
297
|
+
* project stack > profile > user > builtin precedence — the same precedence
|
|
298
|
+
* order used for memory resolution. A kind or ladder cell declared at a
|
|
299
|
+
* more-specific scope shadows the same key from a less-specific scope; the
|
|
300
|
+
* project STACK (`findProjectScopeRoots` — every ancestor `.crouter/`,
|
|
301
|
+
* widened by a selected profile's `projects`) layers nearest-root-strongest;
|
|
302
|
+
* a key no scope declares falls through to the builtin default registry
|
|
303
|
+
* (`defaultScopeConfig().kinds` / `.modelLadders`).
|
|
304
|
+
*
|
|
305
|
+
* Layers RAW (un-defaulted) partial config per scope, not `readConfig(scope)`
|
|
306
|
+
* — `readConfig` already fills in every default kind for a scope that
|
|
307
|
+
* customizes even one, so naively overlaying two already-defaulted
|
|
308
|
+
* `ScopeConfig.kinds` objects would let an untouched project-scope kind
|
|
309
|
+
* (silently defaulted) clobber a real user-scope customization of that same
|
|
310
|
+
* kind. Layering the raw partials avoids that.
|
|
311
|
+
*
|
|
312
|
+
* Callers (launch, kind registry) go through this function rather than
|
|
313
|
+
* `readConfig` directly, so `ScopeConfig.kinds`/`modelLadders`/tools/
|
|
314
|
+
* extensions/`availableTo` all honor the same profile + multi-project
|
|
315
|
+
* precedence. */
|
|
316
|
+
export function readMergedLaunchConfig() {
|
|
317
|
+
const defaults = defaultScopeConfig();
|
|
318
|
+
const userRaw = readRawScopeConfig('user');
|
|
319
|
+
const profileRaw = readRawProfileConfig(process.env['CRTR_PROFILE_ID'] || null);
|
|
320
|
+
const projectRawsFarthestFirst = readRawProjectScopeConfigs();
|
|
321
|
+
let kinds = mergeKinds(userRaw?.kinds, defaults.kinds);
|
|
322
|
+
let modelLadders = mergeModelLadders(userRaw?.modelLadders, defaults.modelLadders);
|
|
323
|
+
if (profileRaw !== null) {
|
|
324
|
+
kinds = mergeKinds(profileRaw.kinds, kinds);
|
|
325
|
+
modelLadders = mergeModelLadders(profileRaw.modelLadders, modelLadders);
|
|
326
|
+
}
|
|
327
|
+
for (const raw of projectRawsFarthestFirst) {
|
|
328
|
+
kinds = mergeKinds(raw.kinds, kinds);
|
|
329
|
+
modelLadders = mergeModelLadders(raw.modelLadders, modelLadders);
|
|
330
|
+
}
|
|
331
|
+
return { kinds, modelLadders };
|
|
332
|
+
}
|
|
333
|
+
/** The effective `KindConfig` for one full kind string (top-level, e.g.
|
|
334
|
+
* `developer`, or sub-kind, e.g. `plan/reviewers/security`), across
|
|
335
|
+
* project > user > builtin precedence. Returns `undefined` for a kind no
|
|
336
|
+
* scope registers — existence is deliberately NOT validated here (kind
|
|
337
|
+
* existence/launch-menu enumeration is a Phase 3 caller concern); this only
|
|
338
|
+
* resolves the config for a kind the caller already knows about. */
|
|
339
|
+
export function resolveKindConfig(kind) {
|
|
340
|
+
return readMergedLaunchConfig().kinds[kind];
|
|
341
|
+
}
|
|
342
|
+
/** The sub-kinds available to spawn FROM a given top-level kind — every
|
|
343
|
+
* registered sub-kind (full path contains `/`) whose `availableTo` (default:
|
|
344
|
+
* its own top-level ancestor, e.g. `plan/reviewers/security` defaults to
|
|
345
|
+
* `['plan']`) includes `kind` or `'*'`. The single source both `sys
|
|
346
|
+
* prompt-review --list`'s `subPersonas` metadata and the live sub-persona
|
|
347
|
+
* spawn-menu splice (`core/substrate/render.ts`) read, so the two can never
|
|
348
|
+
* drift apart. Sorted by full kind name for stable rendering. */
|
|
349
|
+
export function subKindsAvailableTo(kind) {
|
|
350
|
+
const registry = readMergedLaunchConfig().kinds;
|
|
351
|
+
const out = [];
|
|
352
|
+
for (const [full, cfg] of Object.entries(registry)) {
|
|
353
|
+
const slash = full.indexOf('/');
|
|
354
|
+
if (slash === -1)
|
|
355
|
+
continue;
|
|
356
|
+
const topAncestor = full.slice(0, slash);
|
|
357
|
+
const availableTo = cfg.availableTo ?? [topAncestor];
|
|
358
|
+
if (!(availableTo.includes('*') || availableTo.includes(kind)))
|
|
359
|
+
continue;
|
|
360
|
+
out.push({ kind: full, whenToUse: cfg.whenToUse });
|
|
361
|
+
}
|
|
362
|
+
return out.sort((a, b) => a.kind.localeCompare(b.kind));
|
|
220
363
|
}
|
|
221
364
|
export function updateConfig(scope, mutate) {
|
|
222
365
|
const cfg = readConfig(scope);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export type FaultLink = 'pi→provider' | 'viewer↔broker' | 'relay↔broker' | 'browser↔relay' | 'cli→exa';
|
|
2
|
-
export type FaultKind = 'rate-limit' | 'overloaded' | 'connection' | 'auth' | 'protocol' | 'other';
|
|
1
|
+
export type FaultLink = 'pi→provider' | 'viewer↔broker' | 'relay↔broker' | 'browser↔relay' | 'cli→exa' | 'daemon→node';
|
|
2
|
+
export type FaultKind = 'rate-limit' | 'overloaded' | 'connection' | 'auth' | 'protocol' | 'other' | 'wedged';
|
|
3
3
|
export type FaultRetryDisposition = 'auto' | 'manual' | 'fatal';
|
|
4
4
|
export type FaultRetryOwner = 'sdk' | 'daemon' | 'client';
|
|
5
5
|
export interface FaultRetry {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
export type InboxTier = 'critical' | 'urgent' | 'normal' | 'deferred';
|
|
2
2
|
export type InboxKind = 'update' | 'urgent' | 'final' | 'message' | 'completed';
|
|
3
|
-
/** A single inbox entry
|
|
3
|
+
/** A single inbox entry: a push pointer (`ref` to a report, no inline body),
|
|
4
|
+
* or a direct message/system notice (short bodies inline via `data.body`;
|
|
5
|
+
* bodies too long to inline spill to `messages/` and carry both `ref` and a
|
|
6
|
+
* bounded inline preview). */
|
|
4
7
|
export interface InboxEntry {
|
|
5
|
-
/** Short stable handle for addressing this entry from the CLI (`feed message
|
|
6
|
-
* <id>`). Set when the entry carries an inline body too long to fully inline
|
|
7
|
-
* in the digest, so the receiver can read the full text back by id. */
|
|
8
|
-
id?: string;
|
|
9
8
|
/** ISO 8601 timestamp of delivery. */
|
|
10
9
|
ts: string;
|
|
11
10
|
/** Node id of the sender, or null for system-generated entries. */
|
|
@@ -14,7 +13,9 @@ export interface InboxEntry {
|
|
|
14
13
|
tier: InboxTier;
|
|
15
14
|
/** Semantic kind of the push event. */
|
|
16
15
|
kind: InboxKind;
|
|
17
|
-
/** Absolute path to the report file
|
|
16
|
+
/** Absolute path to the sender-side report file (push pointer) or, for a
|
|
17
|
+
* direct message/system notice too long to inline, the spilled body file
|
|
18
|
+
* under `messages/`. */
|
|
18
19
|
ref?: string;
|
|
19
20
|
/** First ~80 chars of the body's first line — enough to decide if it matters. */
|
|
20
21
|
label: string;
|
|
@@ -26,8 +27,6 @@ export interface InboxEntry {
|
|
|
26
27
|
* Fills `ts` (current ISO time). Returns the completed entry.
|
|
27
28
|
*/
|
|
28
29
|
export declare function appendInbox(nodeId: string, entry: Omit<InboxEntry, 'ts'>): InboxEntry;
|
|
29
|
-
/** Find one inbox entry by its short `id` handle (see `feed message <id>`). */
|
|
30
|
-
export declare function readInboxEntryById(nodeId: string, id: string): InboxEntry | undefined;
|
|
31
30
|
/**
|
|
32
31
|
* Return all inbox entries strictly after `cursorIso`.
|
|
33
32
|
* When `cursorIso` is undefined, returns every entry in the file.
|
|
@@ -42,10 +41,6 @@ export declare function readCursor(nodeId: string): string | undefined;
|
|
|
42
41
|
* Persist a new cursor ISO for a node's inbox (atomic tmp+rename).
|
|
43
42
|
*/
|
|
44
43
|
export declare function writeCursor(nodeId: string, iso: string): void;
|
|
45
|
-
/** True when an entry's inline body is long enough that the digest will clip it
|
|
46
|
-
* — the trigger for minting an addressable `id` so the full text stays
|
|
47
|
-
* recoverable via `feed message <id>`. */
|
|
48
|
-
export declare function bodyExceedsPreview(e: Pick<InboxEntry, 'data' | 'label'>): boolean;
|
|
49
44
|
/** Clip a body to a bounded preview, reporting whether anything was dropped. */
|
|
50
45
|
export declare function clipBody(body: string): {
|
|
51
46
|
text: string;
|
package/dist/core/feed/inbox.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
// Per-node inbox.jsonl primitive for the pi-native canvas runtime.
|
|
2
2
|
//
|
|
3
|
-
// An inbox entry is a lightweight
|
|
4
|
-
//
|
|
5
|
-
// enough to
|
|
3
|
+
// An inbox entry is a lightweight pointer for a push (~30 tokens, body lives
|
|
4
|
+
// in nodes/<id>/reports/) or carries a direct message inline. A direct
|
|
5
|
+
// message short enough to inline stays entirely in the jsonl line; one that
|
|
6
|
+
// would clip spills its full body to nodes/<id>/messages/ and the entry
|
|
7
|
+
// carries `ref` to it alongside a bounded inline preview.
|
|
6
8
|
//
|
|
7
9
|
// Layout:
|
|
8
10
|
// nodes/<id>/inbox.jsonl — one JSON line per entry, append-only
|
|
9
11
|
// nodes/<id>/inbox.jsonl.cursor — ISO 8601 of last-read entry (sidecar)
|
|
10
12
|
import { appendFileSync, existsSync, readFileSync, writeFileSync, renameSync, mkdirSync, } from 'node:fs';
|
|
11
|
-
import { dirname } from 'node:path';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
12
14
|
import { randomUUID } from 'node:crypto';
|
|
13
|
-
import { inboxPath } from '../canvas/index.js';
|
|
15
|
+
import { inboxPath, messagesDir } from '../canvas/index.js';
|
|
14
16
|
// ---------------------------------------------------------------------------
|
|
15
17
|
// Cursor sidecar path
|
|
16
18
|
// ---------------------------------------------------------------------------
|
|
@@ -20,16 +22,42 @@ function cursorPath(nodeId) {
|
|
|
20
22
|
// ---------------------------------------------------------------------------
|
|
21
23
|
// Append
|
|
22
24
|
// ---------------------------------------------------------------------------
|
|
25
|
+
/** Spill an oversized inline body to a durable file — the SAME atomic
|
|
26
|
+
* tmp+rename pattern a push report uses — and return its absolute path.
|
|
27
|
+
* Called only when the body would clip in the digest; short bodies never
|
|
28
|
+
* touch disk beyond the jsonl line itself. */
|
|
29
|
+
function writeMessageBody(nodeId, ts, body) {
|
|
30
|
+
const dir = messagesDir(nodeId);
|
|
31
|
+
if (!existsSync(dir))
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
const stamp = ts.replace(/[^0-9]/g, '');
|
|
34
|
+
const finalPath = join(dir, `${stamp}-${randomUUID().slice(0, 6)}.md`);
|
|
35
|
+
const tmpPath = `${finalPath}.tmp`;
|
|
36
|
+
writeFileSync(tmpPath, body, 'utf8');
|
|
37
|
+
renameSync(tmpPath, finalPath);
|
|
38
|
+
return finalPath;
|
|
39
|
+
}
|
|
23
40
|
/**
|
|
24
41
|
* Atomically append one inbox entry to `nodes/<nodeId>/inbox.jsonl`.
|
|
25
42
|
* Fills `ts` (current ISO time). Returns the completed entry.
|
|
26
43
|
*/
|
|
27
44
|
export function appendInbox(nodeId, entry) {
|
|
28
45
|
const full = { ts: new Date().toISOString(), ...entry };
|
|
29
|
-
// A
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
// A push pointer already carries `ref` (the report file) — its body lives
|
|
47
|
+
// there, never inline. A direct message / system notice has no ref yet: when
|
|
48
|
+
// its inline body would clip in the digest, spill the FULL text to a durable
|
|
49
|
+
// file (same atomic tmp+rename as a report) and point `ref` at it, exactly
|
|
50
|
+
// like a push, then keep only the bounded preview inline. A short body is
|
|
51
|
+
// left fully inline, untouched — uniform with how a push's body always
|
|
52
|
+
// lives in a file, never inline, dereferenced by reading `ref`.
|
|
53
|
+
if (full.ref === undefined) {
|
|
54
|
+
const body = inlineBody(full);
|
|
55
|
+
const { text, clipped } = clipBody(body);
|
|
56
|
+
if (clipped) {
|
|
57
|
+
full.ref = writeMessageBody(nodeId, full.ts, body);
|
|
58
|
+
full.data = { ...full.data, body: text };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
33
61
|
const line = JSON.stringify(full) + '\n';
|
|
34
62
|
// Ensure the parent directory exists (inbox.jsonl lives directly under the
|
|
35
63
|
// node dir, which ensureNodeDirs creates — but guard anyway for callers that
|
|
@@ -46,10 +74,6 @@ export function appendInbox(nodeId, entry) {
|
|
|
46
74
|
// ---------------------------------------------------------------------------
|
|
47
75
|
// Read
|
|
48
76
|
// ---------------------------------------------------------------------------
|
|
49
|
-
/** Find one inbox entry by its short `id` handle (see `feed message <id>`). */
|
|
50
|
-
export function readInboxEntryById(nodeId, id) {
|
|
51
|
-
return readInboxSince(nodeId, undefined).find((e) => e.id === id);
|
|
52
|
-
}
|
|
53
77
|
/**
|
|
54
78
|
* Return all inbox entries strictly after `cursorIso`.
|
|
55
79
|
* When `cursorIso` is undefined, returns every entry in the file.
|
|
@@ -108,13 +132,6 @@ function inlineBody(e) {
|
|
|
108
132
|
const body = typeof e.data?.['body'] === 'string' ? e.data['body'].trim() : '';
|
|
109
133
|
return body === '' || body === e.label ? '' : body;
|
|
110
134
|
}
|
|
111
|
-
/** True when an entry's inline body is long enough that the digest will clip it
|
|
112
|
-
* — the trigger for minting an addressable `id` so the full text stays
|
|
113
|
-
* recoverable via `feed message <id>`. */
|
|
114
|
-
export function bodyExceedsPreview(e) {
|
|
115
|
-
const body = inlineBody(e);
|
|
116
|
-
return body !== '' && clipBody(body).clipped;
|
|
117
|
-
}
|
|
118
135
|
/** Clip a body to a bounded preview, reporting whether anything was dropped. */
|
|
119
136
|
export function clipBody(body) {
|
|
120
137
|
let text = body;
|
|
@@ -135,10 +152,11 @@ export function clipBody(body) {
|
|
|
135
152
|
*
|
|
136
153
|
* A push pointer (has a `ref`) stays a pointer — the body lives in the report
|
|
137
154
|
* file, dereferenced on demand by reading that path. A direct `node msg` or
|
|
138
|
-
* system alert
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
155
|
+
* system alert without a `ref` inlines its full body (already bounded — a
|
|
156
|
+
* clipping body is spilled to a file and `ref`'d at append time, see
|
|
157
|
+
* appendInbox). When an entry DOES carry both an inline preview and a `ref`
|
|
158
|
+
* (the clipped case), the tail points at the ref path, same shape as a push —
|
|
159
|
+
* read the file for the rest, no special dereference verb.
|
|
142
160
|
*/
|
|
143
161
|
function renderEntry(e) {
|
|
144
162
|
const body = inlineBody(e);
|
|
@@ -149,11 +167,9 @@ function renderEntry(e) {
|
|
|
149
167
|
}
|
|
150
168
|
const { text, clipped } = clipBody(body);
|
|
151
169
|
const indented = text.split('\n').map((l) => ` ${l}`).join('\n');
|
|
152
|
-
const more =
|
|
153
|
-
? (e.
|
|
154
|
-
|
|
155
|
-
: '\n … (body clipped)')
|
|
156
|
-
: '';
|
|
170
|
+
const more = e.ref !== undefined
|
|
171
|
+
? `\n … (full body: ${e.ref})`
|
|
172
|
+
: clipped ? '\n … (body clipped)' : '';
|
|
157
173
|
return ` [${e.kind}]\n${indented}${more}`;
|
|
158
174
|
}
|
|
159
175
|
/**
|
|
@@ -129,7 +129,7 @@ export function buildBootstrapScript(opts) {
|
|
|
129
129
|
}
|
|
130
130
|
appendOauthAdapterCachePrime(lines, opts.guestHome);
|
|
131
131
|
// Tmpfs sock/pidfile dirs are already mkdir'd above, before this line.
|
|
132
|
-
lines.push(`${shellQuote(BAKED_CRTR_PATH)}
|
|
132
|
+
lines.push(`${shellQuote(BAKED_CRTR_PATH)} sys daemon start </dev/null >>"$HOME/.crtr-runtime/daemon.log" 2>&1`);
|
|
133
133
|
if (opts.isFirstProvision) {
|
|
134
134
|
lines.push('home_node_output="$(', `${shellQuote(BAKED_CRTR_PATH)} --json node new --root --kind general --name home <<'SEED'`, 'Hearth home seed for the Blaxel home backend.', 'SEED', ')"', "home_node_id=\"$(node -e 'const o = JSON.parse(process.argv[1]); process.stdout.write(String(o.node_id ?? o.nodeId ?? o.id ?? \"\"));' \"$home_node_output\")\"", 'if [ -z "$home_node_id" ]; then', ' printf "failed to parse home node id from crtr node new output\n%s\n" "$home_node_output" >&2', ' exit 1', 'fi', 'printf "__HEARTH_NODE_ID__=%s\n" "$home_node_id"',
|
|
135
135
|
// The provisioner polls this file (background exec stdout is not captured).
|
|
@@ -2,18 +2,20 @@ import type { InstalledPlugin, Scope } from '../types.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Thin memory-document resolver for the document substrate. Resolution is scope
|
|
4
4
|
* + leaf/path only: qualifier parse → scope precedence → direct path lookup →
|
|
5
|
-
* leaf-name fallback. The
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* leaf-name fallback. The memory scopes resolve in precedence order project
|
|
6
|
+
* stack > profile > user > builtin. It returns the raw parsed frontmatter +
|
|
7
|
+
* body; it does NOT interpret the schema, kind, gate, or rungs — that is the
|
|
8
|
+
* schema/gate layer's job (callers filter by `frontmatter.kind`). Project
|
|
9
|
+
* resolution is a nearest-first stack of every ancestor `.crouter/` (widened by
|
|
10
|
+
* a selected profile's `projects` manifest entries — see `findProjectScopeRoots`
|
|
11
|
+
* in scope.ts), profile is the selected profile's own singleton `memory/` store
|
|
12
|
+
* (from `CRTR_PROFILE_ID`), and user/builtin remain singleton scopes.
|
|
11
13
|
*/
|
|
12
14
|
export interface MemoryDoc {
|
|
13
15
|
/** Path-derived identity: the doc's path under the scope's memory/ root, no
|
|
14
16
|
* extension, slash-separated — e.g. memory/taste/foo.md → "taste/foo". */
|
|
15
17
|
name: string;
|
|
16
|
-
scope:
|
|
18
|
+
scope: MemoryScope;
|
|
17
19
|
/** Absolute path to the resolved .md file. */
|
|
18
20
|
path: string;
|
|
19
21
|
/** Raw, uncoerced frontmatter record (null when the doc has no frontmatter). */
|
|
@@ -21,26 +23,35 @@ export interface MemoryDoc {
|
|
|
21
23
|
/** Document body, with the frontmatter block stripped. */
|
|
22
24
|
body: string;
|
|
23
25
|
}
|
|
26
|
+
/** The memory-only scope union: the global `Scope` (`user|project|builtin`)
|
|
27
|
+
* plus `profile`. Confined to the memory resolver/list/read/write/render
|
|
28
|
+
* paths — deliberately NOT folded into the global `Scope` union, because that
|
|
29
|
+
* would leak `profile` into config, plugin, marketplace, view, and doctor/
|
|
30
|
+
* sys-config surfaces the spec does not extend. */
|
|
31
|
+
export type MemoryScope = Scope | 'profile';
|
|
24
32
|
export interface MemoryResolutionOpts {
|
|
25
33
|
/** Restrict resolution to a single scope. Conflicts with a scope prefix on
|
|
26
34
|
* the identifier (e.g. `user/foo` with scope=project) throw. */
|
|
27
|
-
scope?:
|
|
35
|
+
scope?: MemoryScope;
|
|
28
36
|
}
|
|
29
37
|
/** Canonical, unambiguous identifier for a memory document: `<scope>/<name>`. */
|
|
30
38
|
export declare function memoryDocId(doc: MemoryDoc): string;
|
|
31
39
|
/** All native memory docs for a scope. Project scope is a nearest-first stack of
|
|
32
|
-
* every ancestor `.crouter/memory
|
|
33
|
-
|
|
40
|
+
* every ancestor `.crouter/memory/` (widened by a selected profile's project
|
|
41
|
+
* stack); profile is the selected profile's own singleton store, resolved
|
|
42
|
+
* through `loadProfileManifest`; user and builtin are singleton stores. */
|
|
43
|
+
export declare function listMemoryDocs(scope: MemoryScope, quiet?: boolean): MemoryDoc[];
|
|
34
44
|
/** All of one plugin's substrate docs, mounted under the virtual `<pluginName>/`
|
|
35
45
|
* namespace. Walks `pluginMemoryDir(plugin)` recursively for *.md, deriving each
|
|
36
46
|
* doc's name exactly as `listMemoryDocs` does (path-relative, no extension,
|
|
37
47
|
* slash-separated) then prefixing the plugin name. Builtin has no plugins. */
|
|
38
|
-
export declare function listPluginMemoryDocs(plugin: InstalledPlugin, scope:
|
|
48
|
+
export declare function listPluginMemoryDocs(plugin: InstalledPlugin, scope: MemoryScope, quiet?: boolean): MemoryDoc[];
|
|
39
49
|
/** All memory docs across the resolved sources, in precedence order: each
|
|
40
|
-
* ancestor project `.crouter/` from nearest to farthest, then
|
|
41
|
-
*
|
|
42
|
-
* docs, so native wins on the
|
|
43
|
-
|
|
50
|
+
* ancestor project `.crouter/` from nearest to farthest, then the selected
|
|
51
|
+
* profile's memory (if any), then user, then builtin. Within each source,
|
|
52
|
+
* native docs are emitted before enabled-plugin docs, so native wins on the
|
|
53
|
+
* caller's first-wins dedup. */
|
|
54
|
+
export declare function listAllMemoryDocs(scope?: MemoryScope, quiet?: boolean): MemoryDoc[];
|
|
44
55
|
/**
|
|
45
56
|
* Resolve a path-derived name to a single memory document.
|
|
46
57
|
*
|