@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
|
@@ -5,6 +5,10 @@ const POLICIES = {
|
|
|
5
5
|
auth: { kind: 'auth', gate: 'none', max: 0, baseDelayMs: 0, capDelayMs: 0, fatal: true },
|
|
6
6
|
protocol: { kind: 'protocol', gate: 'none', max: 0, baseDelayMs: 0, capDelayMs: 0, fatal: true },
|
|
7
7
|
other: { kind: 'other', gate: 'none', max: 0, baseDelayMs: 0, capDelayMs: 0, fatal: true },
|
|
8
|
+
// A wedge is a daemon-detected stall, never auto-retried — recovery is a
|
|
9
|
+
// deliberate agent/operator decision (kill the runaway subprocess), not a
|
|
10
|
+
// provider back-off.
|
|
11
|
+
wedged: { kind: 'wedged', gate: 'none', max: 0, baseDelayMs: 0, capDelayMs: 0, fatal: true },
|
|
8
12
|
};
|
|
9
13
|
export function policyFor(kind) {
|
|
10
14
|
return POLICIES[kind];
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
// crtr [dir] → root pinned to dir
|
|
5
5
|
// crtr [dir] ["prompt"] → root with a starter prompt
|
|
6
6
|
// crtr --name NAME ... → named root
|
|
7
|
+
// crtr --profile <id-or-name> → root under an explicit profile (else the
|
|
8
|
+
// startup selector: MRU covering cwd, else a
|
|
9
|
+
// synchronous create-or-root prompt)
|
|
7
10
|
// crtr <subcommand> ... → falls through to the normal dispatcher
|
|
8
11
|
// crtr -h | --help → root help (dispatcher)
|
|
9
12
|
//
|
|
@@ -25,13 +28,14 @@ function isDir(p) {
|
|
|
25
28
|
* positional dir/prompt). A leading token in this set still boots a root —
|
|
26
29
|
* without it, `crtr --name X` would fall through to the dispatcher and error as
|
|
27
30
|
* an unknown subcommand. */
|
|
28
|
-
const FRONT_DOOR_FLAGS = new Set(['--name', '--kind']);
|
|
31
|
+
const FRONT_DOOR_FLAGS = new Set(['--name', '--kind', '--profile']);
|
|
29
32
|
/** Parse `[dir] [prompt]` positionals + the front-door flags out of the leftover
|
|
30
33
|
* tokens after the bare `crtr`. */
|
|
31
34
|
function parseRootArgs(tokens) {
|
|
32
35
|
let cwd = process.cwd();
|
|
33
36
|
let name;
|
|
34
37
|
let kind;
|
|
38
|
+
let profile;
|
|
35
39
|
const positionals = [];
|
|
36
40
|
for (let i = 0; i < tokens.length; i++) {
|
|
37
41
|
const t = tokens[i];
|
|
@@ -41,6 +45,9 @@ function parseRootArgs(tokens) {
|
|
|
41
45
|
else if (t === '--kind') {
|
|
42
46
|
kind = tokens[++i];
|
|
43
47
|
}
|
|
48
|
+
else if (t === '--profile') {
|
|
49
|
+
profile = tokens[++i];
|
|
50
|
+
}
|
|
44
51
|
else if (t.startsWith('--')) {
|
|
45
52
|
// ignore unknown flags for the front door
|
|
46
53
|
}
|
|
@@ -53,7 +60,7 @@ function parseRootArgs(tokens) {
|
|
|
53
60
|
cwd = resolvePath(positionals.shift());
|
|
54
61
|
}
|
|
55
62
|
const prompt = positionals.length > 0 ? positionals.join(' ') : undefined;
|
|
56
|
-
return { cwd, prompt, name, kind };
|
|
63
|
+
return { cwd, prompt, name, kind, profile };
|
|
57
64
|
}
|
|
58
65
|
/** Env marker set on every pi the front door boots. Its presence means we are
|
|
59
66
|
* already inside a front-door-booted root, so a nested front-door launch must
|
|
@@ -107,8 +107,8 @@ export function drainBearings(meta) {
|
|
|
107
107
|
// Consume the one-shot yield note (deleted on read) BEFORE the kickoff lists
|
|
108
108
|
// the context dir, so it never shows up there.
|
|
109
109
|
const yieldMsg = consumeYieldMessage(nodeId);
|
|
110
|
-
// Drain the feed: read unread since the cursor and advance it past them, so
|
|
111
|
-
//
|
|
110
|
+
// Drain the feed: read unread since the cursor and advance it past them, so
|
|
111
|
+
// only entries that arrive after this point still count as unread.
|
|
112
112
|
const cursor = readCursor(nodeId);
|
|
113
113
|
const entries = readInboxSince(nodeId, cursor);
|
|
114
114
|
let unreadDigest = null;
|
|
@@ -178,10 +178,8 @@ function feedBlock(nodeId, unreadDigest) {
|
|
|
178
178
|
const subj = n === 1 ? 'it is' : 'they are';
|
|
179
179
|
const pron = n === 1 ? 'it' : 'they';
|
|
180
180
|
const verb = n === 1 ? 'pushes' : 'push';
|
|
181
|
-
// State aliveness + the automatic wake at the source
|
|
182
|
-
//
|
|
183
|
-
// burned a turn on `feed read`/`feed peek` to confirm. Asserting it here
|
|
184
|
-
// removes the reason to check.
|
|
181
|
+
// State aliveness + the automatic wake at the source explicitly, so there is
|
|
182
|
+
// no reason to check, poll, or verify separately.
|
|
185
183
|
lines.push(`Awaiting ${n} node${n === 1 ? '' : 's'} — ${subj} alive and running right now, and will wake you the moment ${pron} ${verb}. The wake is automatic; nothing to check, poll, or verify.`);
|
|
186
184
|
for (const m of awaiting)
|
|
187
185
|
lines.push(` - ${m.name} (${m.node_id}) — ${m.status}`);
|
|
@@ -203,7 +201,7 @@ function feedBlock(nodeId, unreadDigest) {
|
|
|
203
201
|
lines.push('', `Report history on disk (collapsed): ${total} report${total === 1 ? '' : 's'} across ${history.length} subscribed node${history.length === 1 ? '' : 's'}. This is a catch-up index for reports your cursor already passed; read only what matters.`, ...visible.map((h) => {
|
|
204
202
|
const ref = `${h.nodeId}:reports/${h.latest}`;
|
|
205
203
|
return ` - ${h.name} (${h.nodeId}): ${h.count} report${h.count === 1 ? '' : 's'}; latest ${reportKind(h.latest)}: ${ref}`;
|
|
206
|
-
}), ...(hidden > 0 ? [` - … ${hidden} more subscribed node${hidden === 1 ? '' : 's'} with reports hidden`] : []), 'Expand: `crtr canvas history read <ref>` for one listed report; `crtr canvas history show <node-id> --type report` for that node\'s full report list; `crtr
|
|
204
|
+
}), ...(hidden > 0 ? [` - … ${hidden} more subscribed node${hidden === 1 ? '' : 's'} with reports hidden`] : []), 'Expand: `crtr canvas history read <ref>` for one listed report; `crtr canvas history show <node-id> --type report` for that node\'s full report list; `crtr canvas history show <node-id> --type inbox` for cursor-independent inbox history including full message bodies.');
|
|
207
205
|
}
|
|
208
206
|
return `<feed>\n${lines.join('\n')}\n</feed>`;
|
|
209
207
|
}
|
|
@@ -49,13 +49,31 @@ export declare function equivalentOtherProviderModel(currentModel: string, faile
|
|
|
49
49
|
* strong/medium/light, ALWAYS anthropic (ignores the env/config default).
|
|
50
50
|
* 4. Anything else → pass through unchanged. */
|
|
51
51
|
export declare function normalizeModel(model: string): string;
|
|
52
|
-
/**
|
|
53
|
-
*
|
|
54
|
-
* (
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
52
|
+
/** The minimal static system prompt every node launches with — a thin launcher
|
|
53
|
+
* frame keeping the `\n\nGuidelines:` anchor the `before_agent_start` substrate
|
|
54
|
+
* hook (canvas-doc-substrate.ts) splices its `<memory kind="preference">` block
|
|
55
|
+
* before. ALL runtime/spine/lifecycle/kind prompting now lives as gated builtin
|
|
56
|
+
* memory docs (src/builtin-memory/), spliced in fresh every turn — this scaffold
|
|
57
|
+
* must never duplicate that prose. Exported so `sys prompt-review` can render
|
|
58
|
+
* through the exact same static+spliced pipeline boot uses, so the two can
|
|
59
|
+
* never drift. */
|
|
60
|
+
export declare const LAUNCH_SYSTEM_PROMPT: string;
|
|
61
|
+
/** Compose a node's full pi launch recipe from the kind registry. The system
|
|
62
|
+
* prompt is now a minimal static scaffold (`LAUNCH_SYSTEM_PROMPT`) — runtime/
|
|
63
|
+
* spine/lifecycle/kind prose is spliced per-turn from gated builtin memory docs,
|
|
64
|
+
* not baked in here. Launch KNOBS (model/tools/extensions) still come from the
|
|
65
|
+
* kind registry (`resolveKindConfig`), keyed on the FULL kind string (top-level
|
|
66
|
+
* or sub-kind). Model precedence, strongest to weakest:
|
|
67
|
+
* 1. Caller-supplied `opts.model` (durable on `meta.model_override`, re-passed
|
|
68
|
+
* on every polymorph).
|
|
69
|
+
* 2. The kind's registered model — mode-aware: orchestrator mode prefers
|
|
70
|
+
* `orchestratorModel`, falling back to `model`; base mode always uses
|
|
71
|
+
* `model`.
|
|
72
|
+
* 3. Unset → pi default.
|
|
73
|
+
* Callers pass the authoritative lifecycle + hasManager (`parent !== null`) so
|
|
74
|
+
* a polymorph/flip rebuilds the recipe faithfully; `lifecycle` is returned
|
|
75
|
+
* as-given — it is no longer persona-frontmatter-derived. The two canvas
|
|
76
|
+
* extensions are always first; kind-declared extensions follow. */
|
|
59
77
|
export declare function buildLaunchSpec(kind: string, mode: Mode, opts: {
|
|
60
78
|
lifecycle: Lifecycle;
|
|
61
79
|
hasManager: boolean;
|
|
@@ -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).
|
|
@@ -38,7 +38,7 @@ import { isPidAlive } from '../canvas/pid.js';
|
|
|
38
38
|
// verbs — respawnPane, respawnPaneDetached, swapPaneInPlace, breakPaneToSession,
|
|
39
39
|
// joinPane, setWindowOption, setRemainOnExit — are deleted). `breakPane` +
|
|
40
40
|
// `selectLayout` are FRESH minimal viewer-chrome helpers (not the deleted
|
|
41
|
-
// engine-in-pane `breakPaneToSession`): `
|
|
41
|
+
// engine-in-pane `breakPaneToSession`): `surface tmux-spread` breaks the caller's
|
|
42
42
|
// viewer pane into its own window and tiles sibling viewers beside it.
|
|
43
43
|
// `piCommand` + `paneCurrentPath` are KEPT: the sessionless surface-view system
|
|
44
44
|
// (view-cycle.ts / view-run.ts) builds its pane commands with them, and the §5.1
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
//
|
|
18
18
|
// reviveNode remains the ONLY sanctioned launcher of the node engine.
|
|
19
19
|
import { existsSync } from 'node:fs';
|
|
20
|
-
import { getNode, updateNode, clearPid, fullName, cancelDeadlinesFor, } from '../canvas/index.js';
|
|
20
|
+
import { getNode, updateNode, clearPid, fullName, cancelDeadlinesFor, subscribersOf, } from '../canvas/index.js';
|
|
21
21
|
import { transition } from './lifecycle.js';
|
|
22
|
+
import { fanDoctrineWake } from './close.js';
|
|
22
23
|
import { buildPiArgv } from './launch.js';
|
|
23
24
|
import { buildReviveKickoff, drainBearings } from './kickoff.js';
|
|
24
25
|
import { headlessBrokerHost } from './host.js';
|
|
@@ -163,6 +164,13 @@ export function reviveNode(nodeId, opts) {
|
|
|
163
164
|
catch (error) {
|
|
164
165
|
try {
|
|
165
166
|
transition(nodeId, 'crash');
|
|
167
|
+
// Doctrine wake: a launch-refusal here marks the node dead with NOTHING
|
|
168
|
+
// else fanning that out — the daemon's own auto-revive is the most common
|
|
169
|
+
// caller (superviseTick, best-effort try/catch, stderr-only on failure), so
|
|
170
|
+
// without this a manager waiting dormant on this child hangs forever,
|
|
171
|
+
// never told it will not come back on its own. Mirrors closeNode's step-4
|
|
172
|
+
// fan-out (same subscribers table, same active/passive split).
|
|
173
|
+
fanDoctrineWake(nodeId, subscribersOf(nodeId), `Child crashed — ${fullName(meta)} (${nodeId}) failed to relaunch and is now dead. It will NOT resume on its own.`, { reason: 'child-crashed', child: nodeId });
|
|
166
174
|
}
|
|
167
175
|
catch {
|
|
168
176
|
/* best-effort cleanup */
|
|
@@ -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.
|
|
@@ -78,7 +78,7 @@ export declare function renameWindow(window: string, name: string): boolean;
|
|
|
78
78
|
* the break never falls back to tmux's global current pane. The pane keeps its
|
|
79
79
|
* durable `%id` across the move (only its window changes), so callers can keep
|
|
80
80
|
* using the same pane handle. Returns the new `{window, pane}` location, or null
|
|
81
|
-
* if tmux fails. Used by `
|
|
81
|
+
* if tmux fails. Used by `surface tmux-spread` to lift the caller's viewer into a
|
|
82
82
|
* fresh window before tiling sibling viewers beside it. */
|
|
83
83
|
export declare function breakPane(pane: string): {
|
|
84
84
|
window: string;
|
|
@@ -187,7 +187,7 @@ export declare function windowAlive(session: string | null | undefined, window:
|
|
|
187
187
|
* to `tmux select-window -t <session>:<window>`. Best-effort; never throws. */
|
|
188
188
|
export declare function selectWindow(session: string, window: string): boolean;
|
|
189
189
|
/** Apply a named tmux layout to a window (`tmux select-layout -t <window>
|
|
190
|
-
* <layout>`). `
|
|
190
|
+
* <layout>`). `surface tmux-spread` calls it with `tiled` to evenly grid every
|
|
191
191
|
* viewer pane in the spread window (and between splits, to redistribute space so
|
|
192
192
|
* the next split has room). Best-effort; false if tmux fails. */
|
|
193
193
|
export declare function selectLayout(window: string, layout: string): boolean;
|
|
@@ -176,7 +176,7 @@ export function renameWindow(window, name) {
|
|
|
176
176
|
* the break never falls back to tmux's global current pane. The pane keeps its
|
|
177
177
|
* durable `%id` across the move (only its window changes), so callers can keep
|
|
178
178
|
* using the same pane handle. Returns the new `{window, pane}` location, or null
|
|
179
|
-
* if tmux fails. Used by `
|
|
179
|
+
* if tmux fails. Used by `surface tmux-spread` to lift the caller's viewer into a
|
|
180
180
|
* fresh window before tiling sibling viewers beside it. */
|
|
181
181
|
export function breakPane(pane) {
|
|
182
182
|
const r = tmux([
|
|
@@ -369,7 +369,7 @@ export function selectWindow(session, window) {
|
|
|
369
369
|
return tmux(['select-window', '-t', `${session}:${window}`]).ok;
|
|
370
370
|
}
|
|
371
371
|
/** Apply a named tmux layout to a window (`tmux select-layout -t <window>
|
|
372
|
-
* <layout>`). `
|
|
372
|
+
* <layout>`). `surface tmux-spread` calls it with `tiled` to evenly grid every
|
|
373
373
|
* viewer pane in the spread window (and between splits, to redistribute space so
|
|
374
374
|
* the next split has room). Best-effort; false if tmux fails. */
|
|
375
375
|
export function selectLayout(window, layout) {
|
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;
|