@crouton-kit/crouter 0.3.18 → 0.3.20
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 +6 -0
- package/dist/builtin-memory/crouter-development/marketplaces.md +164 -0
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +58 -0
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +60 -0
- package/dist/builtin-memory/crouter-development/personas.md +117 -0
- package/dist/builtin-memory/crouter-development/plugins.md +163 -0
- package/dist/builtin-memory/design.md +60 -0
- package/dist/builtin-memory/development.md +116 -0
- package/dist/builtin-memory/planning.md +66 -0
- package/dist/builtin-memory/spec.md +99 -0
- package/dist/builtin-personas/design/PERSONA.md +1 -0
- package/dist/builtin-personas/design/orchestrator.md +2 -1
- package/dist/builtin-personas/developer/PERSONA.md +1 -0
- package/dist/builtin-personas/developer/orchestrator.md +2 -1
- package/dist/builtin-personas/explore/PERSONA.md +1 -0
- package/dist/builtin-personas/explore/orchestrator.md +4 -0
- package/dist/builtin-personas/general/PERSONA.md +1 -0
- package/dist/builtin-personas/general/orchestrator.md +4 -0
- package/dist/builtin-personas/lifecycle/resident.md +1 -1
- package/dist/builtin-personas/lifecycle/terminal.md +5 -2
- package/dist/builtin-personas/orchestration-kernel.md +15 -24
- package/dist/builtin-personas/plan/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/orchestrator.md +2 -1
- package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -0
- package/dist/builtin-personas/review/PERSONA.md +1 -0
- package/dist/builtin-personas/review/orchestrator.md +4 -0
- package/dist/builtin-personas/runtime-base.md +6 -3
- package/dist/builtin-personas/spec/PERSONA.md +1 -0
- package/dist/builtin-personas/spec/orchestrator.md +2 -1
- package/dist/builtin-personas/waiting.md +8 -0
- package/dist/builtin-skills/skills/crouter-development/personas/orchestrator-prompt/SKILL.md +1 -1
- package/dist/builtin-skills/skills/crouter-development/plugins/SKILL.md +2 -2
- package/dist/builtin-views/_lib/states.mjs +161 -0
- package/dist/builtin-views/canvas/client.mjs +303 -0
- package/dist/builtin-views/canvas/view.mjs +576 -0
- package/dist/builtin-views/git-pr/client.mjs +440 -0
- package/dist/builtin-views/git-pr/view.mjs +675 -0
- package/dist/builtin-views/inbox/_lib/render.mjs +177 -0
- package/dist/builtin-views/inbox/sources/gmail.mjs +965 -0
- package/dist/builtin-views/inbox/sources/linkedin.mjs +427 -0
- package/dist/builtin-views/inbox/view.mjs +889 -0
- package/dist/builtin-views/linkedin/client.mjs +610 -0
- package/dist/builtin-views/linkedin/view.mjs +1171 -0
- package/dist/clients/attach/attach-cmd.d.ts +2 -0
- package/dist/clients/attach/attach-cmd.js +354 -0
- package/dist/clients/attach/chat-view.d.ts +77 -0
- package/dist/clients/attach/chat-view.js +450 -0
- package/dist/clients/attach/clipboard-image.d.ts +16 -0
- package/dist/clients/attach/clipboard-image.js +113 -0
- package/dist/clients/attach/config-load.d.ts +31 -0
- package/dist/clients/attach/config-load.js +113 -0
- package/dist/clients/attach/extension-dialogs.d.ts +29 -0
- package/dist/clients/attach/extension-dialogs.js +101 -0
- package/dist/clients/attach/input-controller.d.ts +54 -0
- package/dist/clients/attach/input-controller.js +204 -0
- package/dist/clients/attach/slash-commands.d.ts +36 -0
- package/dist/clients/attach/slash-commands.js +200 -0
- package/dist/clients/attach/view-socket.d.ts +48 -0
- package/dist/clients/attach/view-socket.js +126 -0
- package/dist/commands/attention.js +3 -3
- package/dist/commands/canvas-prune.js +1 -1
- package/dist/commands/daemon.js +3 -2
- package/dist/commands/human/prompts.js +1 -1
- package/dist/commands/human/queue.js +54 -8
- package/dist/commands/human/shared.d.ts +2 -0
- package/dist/commands/memory/find.d.ts +1 -0
- package/dist/commands/memory/find.js +180 -0
- package/dist/commands/memory/lint.d.ts +1 -0
- package/dist/commands/memory/lint.js +140 -0
- package/dist/commands/memory/list.d.ts +1 -0
- package/dist/commands/memory/list.js +79 -0
- package/dist/commands/memory/read.js +103 -0
- package/dist/commands/memory/shared.d.ts +30 -0
- package/dist/commands/memory/shared.js +122 -0
- package/dist/commands/memory/write.d.ts +1 -0
- package/dist/commands/memory/write.js +85 -0
- package/dist/commands/memory.d.ts +2 -0
- package/dist/commands/memory.js +27 -0
- package/dist/commands/node.d.ts +3 -2
- package/dist/commands/node.js +610 -61
- package/dist/commands/pkg/market-manage.js +1 -1
- package/dist/commands/push.js +6 -6
- package/dist/commands/revive.js +1 -1
- package/dist/commands/skill/author.js +1 -1
- package/dist/commands/skill/shared.d.ts +1 -8
- package/dist/commands/skill/shared.js +2 -55
- package/dist/commands/skill.js +9 -14
- package/dist/commands/sys/doctor.js +1 -1
- package/dist/commands/sys/update.js +1 -1
- package/dist/commands/view-cycle.d.ts +2 -0
- package/dist/commands/view-cycle.js +125 -0
- package/dist/commands/view-list.d.ts +2 -0
- package/dist/commands/view-list.js +66 -0
- package/dist/commands/view-new.d.ts +2 -0
- package/dist/commands/view-new.js +70 -0
- package/dist/commands/view-pick.d.ts +2 -0
- package/dist/commands/view-pick.js +119 -0
- package/dist/commands/view-run.d.ts +2 -0
- package/dist/commands/view-run.js +191 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +29 -0
- package/dist/core/__tests__/broker-attach-limits.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-limits.test.js +157 -0
- package/dist/core/__tests__/broker-attach-stream.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-stream.test.js +125 -0
- package/dist/core/__tests__/broker-crash-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/broker-crash-teardown.test.js +116 -0
- package/dist/core/__tests__/broker-dialogs.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dialogs.test.js +126 -0
- package/dist/core/__tests__/broker-dormant-wake.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dormant-wake.test.js +51 -0
- package/dist/core/__tests__/broker-lifecycle.test.d.ts +1 -0
- package/dist/core/__tests__/broker-lifecycle.test.js +87 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.d.ts +1 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +166 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.d.ts +1 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +102 -0
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +2 -36
- package/dist/core/__tests__/cascade-close.test.js +12 -2
- package/dist/core/__tests__/child-death-wake.test.d.ts +1 -0
- package/dist/core/__tests__/child-death-wake.test.js +245 -0
- package/dist/core/__tests__/context-intro.test.js +76 -62
- package/dist/core/__tests__/daemon-boot.test.js +14 -5
- package/dist/core/__tests__/daemon-liveness.test.js +34 -9
- package/dist/core/__tests__/detach-focus.test.d.ts +1 -0
- package/dist/core/__tests__/detach-focus.test.js +206 -0
- package/dist/core/__tests__/draw-style.test.d.ts +1 -0
- package/dist/core/__tests__/draw-style.test.js +258 -0
- package/dist/core/__tests__/fixtures/c3-custom-provider-ext.d.ts +2 -0
- package/dist/core/__tests__/fixtures/c3-custom-provider-ext.js +18 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +138 -0
- package/dist/core/__tests__/fixtures/fake-engine.js +614 -0
- package/dist/core/__tests__/fixtures/fake-pi-host.js +1 -0
- package/dist/core/__tests__/flagship-lifecycle.test.js +7 -1
- package/dist/core/__tests__/frame-decoder-perf.test.d.ts +1 -0
- package/dist/core/__tests__/frame-decoder-perf.test.js +198 -0
- package/dist/core/__tests__/helpers/broker-clients.d.ts +43 -0
- package/dist/core/__tests__/helpers/broker-clients.js +178 -0
- package/dist/core/__tests__/helpers/harness.d.ts +9 -0
- package/dist/core/__tests__/helpers/harness.js +111 -1
- package/dist/core/__tests__/home-session.test.js +41 -8
- package/dist/core/__tests__/human-new-window-regression.test.d.ts +1 -0
- package/dist/core/__tests__/human-new-window-regression.test.js +101 -0
- package/dist/core/__tests__/kickoff.test.js +37 -3
- package/dist/core/__tests__/live-mutation-verbs.test.d.ts +1 -0
- package/dist/core/__tests__/live-mutation-verbs.test.js +175 -0
- package/dist/core/__tests__/live-mutation.test.js +6 -132
- package/dist/core/__tests__/memory.test.js +23 -115
- package/dist/core/__tests__/placement-focus.test.js +5 -0
- package/dist/core/__tests__/placement-teardown.test.js +54 -11
- package/dist/core/__tests__/relaunch.test.js +4 -3
- package/dist/core/__tests__/review-render-pane-regression.test.d.ts +1 -0
- package/dist/core/__tests__/review-render-pane-regression.test.js +133 -0
- package/dist/core/__tests__/spawn-root.test.js +10 -0
- package/dist/core/__tests__/spike-harness.test.js +1 -0
- package/dist/core/__tests__/wake-bearings.test.d.ts +1 -0
- package/dist/core/__tests__/wake-bearings.test.js +156 -0
- package/dist/core/__tests__/wake-origin.test.d.ts +1 -0
- package/dist/core/__tests__/wake-origin.test.js +110 -0
- package/dist/core/bootstrap.js +1 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +1 -0
- package/dist/core/canvas/browse/app.js +24 -2
- package/dist/core/canvas/browse/model.d.ts +38 -2
- package/dist/core/canvas/browse/model.js +134 -10
- package/dist/core/canvas/browse/render.d.ts +6 -12
- package/dist/core/canvas/browse/render.js +72 -104
- package/dist/core/canvas/canvas.js +16 -23
- package/dist/core/canvas/db.js +46 -0
- package/dist/core/canvas/index.d.ts +1 -0
- package/dist/core/canvas/index.js +1 -0
- package/dist/core/canvas/pid.d.ts +4 -0
- package/dist/core/canvas/pid.js +23 -0
- package/dist/core/canvas/render.d.ts +11 -2
- package/dist/core/canvas/render.js +69 -0
- package/dist/core/canvas/types.d.ts +91 -3
- package/dist/core/canvas/wakeups.d.ts +76 -0
- package/dist/core/canvas/wakeups.js +185 -0
- package/dist/core/config.js +4 -1
- package/dist/core/frontmatter.js +37 -124
- package/dist/core/memory-resolver.d.ts +49 -0
- package/dist/core/memory-resolver.js +141 -0
- package/dist/core/personas/loader.d.ts +7 -0
- package/dist/core/personas/loader.js +36 -5
- package/dist/core/personas/resolve.js +13 -3
- package/dist/core/predicate.d.ts +63 -0
- package/dist/core/predicate.js +189 -0
- package/dist/core/resolver.js +26 -5
- package/dist/core/runtime/bearings.d.ts +53 -12
- package/dist/core/runtime/bearings.js +132 -59
- package/dist/core/runtime/broker-cli.d.ts +1 -0
- package/dist/core/runtime/broker-cli.js +46 -0
- package/dist/core/runtime/broker-protocol.d.ts +332 -0
- package/dist/core/runtime/broker-protocol.js +153 -0
- package/dist/core/runtime/broker-sdk.d.ts +48 -0
- package/dist/core/runtime/broker-sdk.js +72 -0
- package/dist/core/runtime/broker.d.ts +55 -0
- package/dist/core/runtime/broker.js +1128 -0
- package/dist/core/runtime/close.js +35 -6
- package/dist/core/runtime/host.d.ts +53 -0
- package/dist/core/runtime/host.js +186 -0
- package/dist/core/runtime/kickoff.d.ts +2 -1
- package/dist/core/runtime/kickoff.js +91 -5
- package/dist/core/runtime/launch.d.ts +54 -4
- package/dist/core/runtime/launch.js +85 -5
- package/dist/core/runtime/lifecycle.js +23 -6
- package/dist/core/runtime/memory.d.ts +2 -42
- package/dist/core/runtime/memory.js +11 -162
- package/dist/core/runtime/nodes.d.ts +37 -0
- package/dist/core/runtime/nodes.js +60 -1
- package/dist/core/runtime/persona.js +21 -11
- package/dist/core/runtime/pi-vendored.d.ts +18 -0
- package/dist/core/runtime/pi-vendored.js +49 -0
- package/dist/core/runtime/placement.d.ts +32 -14
- package/dist/core/runtime/placement.js +206 -52
- package/dist/core/runtime/promote.d.ts +0 -6
- package/dist/core/runtime/promote.js +4 -12
- package/dist/core/runtime/{demote.d.ts → recycle.d.ts} +5 -5
- package/dist/core/runtime/{demote.js → recycle.js} +27 -11
- package/dist/core/runtime/reset.js +11 -8
- package/dist/core/runtime/revive.d.ts +2 -0
- package/dist/core/runtime/revive.js +34 -34
- package/dist/core/runtime/spawn.d.ts +23 -0
- package/dist/core/runtime/spawn.js +77 -23
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +6 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +28 -0
- package/dist/core/runtime/tmux.js +80 -6
- package/dist/core/scope.d.ts +11 -0
- package/dist/core/scope.js +39 -0
- package/dist/core/spawn.d.ts +5 -0
- package/dist/core/spawn.js +20 -2
- package/dist/core/substrate/gate.d.ts +13 -0
- package/dist/core/substrate/gate.js +21 -0
- package/dist/core/substrate/index.d.ts +7 -0
- package/dist/core/substrate/index.js +18 -0
- package/dist/core/substrate/on-read.d.ts +14 -0
- package/dist/core/substrate/on-read.js +292 -0
- package/dist/core/substrate/render.d.ts +25 -0
- package/dist/core/substrate/render.js +256 -0
- package/dist/core/substrate/schema.d.ts +76 -0
- package/dist/core/substrate/schema.js +124 -0
- package/dist/core/substrate/session-cache.d.ts +30 -0
- package/dist/core/substrate/session-cache.js +77 -0
- package/dist/core/substrate/subject.d.ts +41 -0
- package/dist/core/substrate/subject.js +54 -0
- package/dist/core/tui/contract.d.ts +83 -0
- package/dist/core/tui/contract.js +8 -0
- package/dist/core/tui/draw.d.ts +96 -0
- package/dist/core/tui/draw.js +339 -0
- package/dist/core/tui/host.d.ts +29 -0
- package/dist/core/tui/host.js +379 -0
- package/dist/core/tui/loader.d.ts +16 -0
- package/dist/core/tui/loader.js +94 -0
- package/dist/core/{canvas/browse → tui}/terminal.js +7 -6
- package/dist/core/wake.d.ts +86 -0
- package/dist/core/wake.js +308 -0
- package/dist/daemon/crtrd.d.ts +29 -4
- package/dist/daemon/crtrd.js +662 -46
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +171 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +19 -12
- package/dist/pi-extensions/canvas-commands.d.ts +3 -0
- package/dist/pi-extensions/canvas-commands.js +10 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +17 -0
- package/dist/pi-extensions/canvas-context-intro.js +55 -15
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +44 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +112 -0
- package/dist/pi-extensions/canvas-nav.d.ts +3 -0
- package/dist/pi-extensions/canvas-nav.js +87 -23
- package/dist/pi-extensions/canvas-stophook.js +17 -8
- package/dist/pi-extensions/canvas-view.d.ts +21 -0
- package/dist/pi-extensions/canvas-view.js +75 -0
- package/dist/prompts/skill.js +19 -26
- package/dist/prompts/view.d.ts +7 -0
- package/dist/prompts/view.js +101 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -0
- package/package.json +9 -4
- package/dist/commands/skill/find.d.ts +0 -4
- package/dist/commands/skill/find.js +0 -257
- package/dist/commands/skill/read.js +0 -91
- /package/dist/commands/{skill → memory}/read.d.ts +0 -0
- /package/dist/core/{canvas/browse → tui}/terminal.d.ts +0 -0
|
@@ -32,15 +32,20 @@ export const CANVAS_NAV_PATH = resolveExtension('canvas-nav');
|
|
|
32
32
|
export const CANVAS_GOAL_CAPTURE_PATH = resolveExtension('canvas-goal-capture');
|
|
33
33
|
export const CANVAS_PASSIVE_CONTEXT_PATH = resolveExtension('canvas-passive-context');
|
|
34
34
|
export const CANVAS_CONTEXT_INTRO_PATH = resolveExtension('canvas-context-intro');
|
|
35
|
+
export const CANVAS_DOC_SUBSTRATE_PATH = resolveExtension('canvas-doc-substrate');
|
|
35
36
|
export const CANVAS_COMMANDS_PATH = resolveExtension('canvas-commands');
|
|
36
37
|
export const CANVAS_RESUME_PATH = resolveExtension('canvas-resume');
|
|
38
|
+
export const CANVAS_VIEW_PATH = resolveExtension('canvas-view');
|
|
37
39
|
/** The canvas extensions every node loads, in order: stophook (routing +
|
|
38
40
|
* telemetry + session-id capture), inbox-watcher (wake), nav (in-editor
|
|
39
41
|
* graph chrome), goal-capture (persist the first user message as the goal),
|
|
40
42
|
* passive-context (drain passive backlog as pre-text on the next message),
|
|
41
43
|
* context-intro (inject the <crtr-context> bearings block as its own session
|
|
42
|
-
* message, once per brand-new chat),
|
|
43
|
-
*
|
|
44
|
+
* message, once per brand-new chat), doc-substrate (the unified document
|
|
45
|
+
* substrate's two hooks: ## Skills + ## Preferences at boot, on-read context
|
|
46
|
+
* injection), commands (the /promote slash-command), resume (the /resume-node
|
|
47
|
+
* whole-canvas picker → `crtr node focus`), view (the /view popup → `crtr
|
|
48
|
+
* view pick` / `crtr view run <name>`).
|
|
44
49
|
* All self-gate on CRTR_NODE_ID. goal-capture precedes passive-context so it
|
|
45
50
|
* reads the raw user text. */
|
|
46
51
|
export const CANVAS_EXTENSIONS = [
|
|
@@ -50,12 +55,27 @@ export const CANVAS_EXTENSIONS = [
|
|
|
50
55
|
CANVAS_GOAL_CAPTURE_PATH,
|
|
51
56
|
CANVAS_PASSIVE_CONTEXT_PATH,
|
|
52
57
|
CANVAS_CONTEXT_INTRO_PATH,
|
|
58
|
+
CANVAS_DOC_SUBSTRATE_PATH,
|
|
53
59
|
CANVAS_COMMANDS_PATH,
|
|
54
60
|
CANVAS_RESUME_PATH,
|
|
61
|
+
CANVAS_VIEW_PATH,
|
|
55
62
|
];
|
|
56
|
-
/**
|
|
57
|
-
*
|
|
63
|
+
/** The named capability tiers a caller picks with `--model`, in descending
|
|
64
|
+
* strength. Each maps to a concrete pi model spec. `strong`/`medium`/`light`
|
|
65
|
+
* resolve to the latest opus/sonnet/haiku; `ultra` to the frontier model. */
|
|
66
|
+
export const MODEL_TIERS = {
|
|
67
|
+
ultra: 'anthropic/claude-fable-5',
|
|
68
|
+
strong: 'anthropic/opus',
|
|
69
|
+
medium: 'anthropic/sonnet',
|
|
70
|
+
light: 'anthropic/haiku',
|
|
71
|
+
};
|
|
72
|
+
/** Resolve a model token to the spec pi gets via `--model`. A named tier
|
|
73
|
+
* (ultra/strong/medium/light) maps to its concrete spec; a bare alias
|
|
74
|
+
* (sonnet/opus/haiku) resolves to the anthropic provider under pi (avoids the
|
|
75
|
+
* bedrock default); anything with a `/` or an unknown name passes through. */
|
|
58
76
|
export function normalizeModel(model) {
|
|
77
|
+
if (model in MODEL_TIERS)
|
|
78
|
+
return MODEL_TIERS[model];
|
|
59
79
|
const bare = new Set(['sonnet', 'opus', 'haiku']);
|
|
60
80
|
if (bare.has(model))
|
|
61
81
|
return `anthropic/${model}`;
|
|
@@ -73,8 +93,12 @@ export function normalizeModel(model) {
|
|
|
73
93
|
* declared extensions follow. */
|
|
74
94
|
export function buildLaunchSpec(kind, mode, opts) {
|
|
75
95
|
const p = resolvePersona(kind, mode, { lifecycle: opts.lifecycle, hasManager: opts.hasManager });
|
|
96
|
+
// A caller-supplied override (durable on `meta.model_override`, re-passed on
|
|
97
|
+
// every polymorph) wins over the persona's declared default; absent both, the
|
|
98
|
+
// model is left unset and the node inherits pi's default.
|
|
99
|
+
const chosenModel = opts.model ?? p.model;
|
|
76
100
|
const launch = {
|
|
77
|
-
model:
|
|
101
|
+
model: chosenModel !== undefined ? normalizeModel(chosenModel) : undefined,
|
|
78
102
|
tools: p.tools,
|
|
79
103
|
extensions: [...CANVAS_EXTENSIONS, ...p.extensions],
|
|
80
104
|
systemPrompt: p.systemPrompt,
|
|
@@ -158,3 +182,59 @@ export function buildPiArgv(meta, opts = {}) {
|
|
|
158
182
|
argv.push(opts.prompt);
|
|
159
183
|
return { argv, env: nodeEnv(meta) };
|
|
160
184
|
}
|
|
185
|
+
/** Translate a `PiInvocation` (the recipe `buildPiArgv` produced) into the SDK
|
|
186
|
+
* config the broker hosts an engine with. This is the EXACT inverse of
|
|
187
|
+
* `buildPiArgv` and must track its flag set 1:1 — co-located here so the two
|
|
188
|
+
* never drift. Safe because `buildPiArgv` is the SOLE producer of these flags
|
|
189
|
+
* (we own both ends). SIDE EFFECT: merges `inv.env` into `process.env` so the
|
|
190
|
+
* in-process engine + the bound canvas extensions see the same env a forked pi
|
|
191
|
+
* would (CRTR_NODE_ID, CRTR_ROOT_SESSION, CRTR_SUBTREE, …). */
|
|
192
|
+
export function piInvocationToSdkConfig(inv) {
|
|
193
|
+
for (const [k, v] of Object.entries(inv.env))
|
|
194
|
+
process.env[k] = v;
|
|
195
|
+
const cfg = {
|
|
196
|
+
cwd: inv.env['CRTR_NODE_CWD'] ?? process.cwd(),
|
|
197
|
+
extensionPaths: [],
|
|
198
|
+
};
|
|
199
|
+
const argv = inv.argv;
|
|
200
|
+
for (let i = 0; i < argv.length; i++) {
|
|
201
|
+
const tok = argv[i];
|
|
202
|
+
switch (tok) {
|
|
203
|
+
case '-e':
|
|
204
|
+
cfg.extensionPaths.push(argv[++i]);
|
|
205
|
+
break;
|
|
206
|
+
case '-n':
|
|
207
|
+
cfg.editorName = argv[++i];
|
|
208
|
+
break;
|
|
209
|
+
case '--fork':
|
|
210
|
+
cfg.forkFrom = argv[++i];
|
|
211
|
+
break;
|
|
212
|
+
case '--session': {
|
|
213
|
+
const v = argv[++i];
|
|
214
|
+
// A path (contains `/` or ends `.jsonl`) resumes via SessionManager.open;
|
|
215
|
+
// a bare uuid is the legacy fallback. Same classification buildPiArgv
|
|
216
|
+
// documents (path preferred, immune to cwd discrepancy).
|
|
217
|
+
if (v.includes('/') || v.endsWith('.jsonl'))
|
|
218
|
+
cfg.resumeSessionPath = v;
|
|
219
|
+
else
|
|
220
|
+
cfg.resumeSessionId = v;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case '--model':
|
|
224
|
+
cfg.model = argv[++i];
|
|
225
|
+
break;
|
|
226
|
+
case '--tools':
|
|
227
|
+
cfg.tools = argv[++i].split(',').filter((t) => t !== '');
|
|
228
|
+
break;
|
|
229
|
+
case '--append-system-prompt':
|
|
230
|
+
cfg.appendSystemPromptPath = argv[++i];
|
|
231
|
+
break;
|
|
232
|
+
default:
|
|
233
|
+
// buildPiArgv consumes every flag's value via the cases above, so any
|
|
234
|
+
// token reaching here is the single trailing positional (the prompt).
|
|
235
|
+
cfg.firstPrompt = tok;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return cfg;
|
|
240
|
+
}
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
//
|
|
16
16
|
// Crash-safety invariant (was a comment repeated in reset/close/reapDescendants):
|
|
17
17
|
// "flip status to a non-supervised value + clear intent BEFORE killing the
|
|
18
|
-
//
|
|
18
|
+
// host" (tmux pane or headless broker) — the daemon only ever revives
|
|
19
|
+
// active|idle nodes, so a teardown must
|
|
19
20
|
// leave the node done/canceled first to close the revive race. That invariant is
|
|
20
21
|
// now the DEFINITION of the `cancel` event: callers flip via transition()
|
|
21
|
-
// and only THEN
|
|
22
|
+
// and only THEN tear the host down.
|
|
22
23
|
//
|
|
23
24
|
// Unification (A5, human-confirmed 2026-06-06): an externally-reaped node — torn
|
|
24
25
|
// down because the user moved on (close cascade) OR because a root reset/relaunch
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
// row UPDATE directly via openDb — the one sanctioned exception to "only
|
|
34
35
|
// canvas.ts touches the db" (see canvas/CLAUDE.md), exactly as db.ts's backfill
|
|
35
36
|
// is the sanctioned exception for a data migration.
|
|
36
|
-
import { openDb, getNode } from '../canvas/index.js';
|
|
37
|
+
import { openDb, getNode, cancelWakesFor, cancelSelfAlarms } from '../canvas/index.js';
|
|
37
38
|
const ANY = '*';
|
|
38
39
|
/** The supervised statuses — a live node the daemon watches. */
|
|
39
40
|
const LIVE = ['active', 'idle'];
|
|
@@ -47,12 +48,12 @@ const TRANSITIONS = {
|
|
|
47
48
|
// of a node that did NOT finish its own work → canceled, intent cleared. (A5:
|
|
48
49
|
// done is reserved for finalize; every external reap unifies on canceled.)
|
|
49
50
|
cancel: { status: 'canceled', intent: null, from: ANY },
|
|
50
|
-
// daemon superviseTick:
|
|
51
|
-
// (the dead log line still reports it).
|
|
51
|
+
// daemon superviseTick: engine container gone (tmux pane or broker process)
|
|
52
|
+
// with no yield/release intent. Intent KEPT (the dead log line still reports it).
|
|
52
53
|
crash: { status: 'dead', from: LIVE },
|
|
53
54
|
// requestYield · relaunchRoot new-node safety net. Status KEPT (already active).
|
|
54
55
|
yield: { intent: 'refresh', from: LIVE },
|
|
55
|
-
// stophook idle-release: free the
|
|
56
|
+
// stophook idle-release: free the host, stay woken by the inbox.
|
|
56
57
|
release: { status: 'idle', intent: 'idle-release', from: LIVE },
|
|
57
58
|
// reviveNode · resetRoot · stophook boot-confirm (clear a pending refresh net).
|
|
58
59
|
revive: { status: 'active', intent: null, from: ANY },
|
|
@@ -90,5 +91,21 @@ export function transition(nodeId, event) {
|
|
|
90
91
|
else if (writeIntent) {
|
|
91
92
|
db.prepare('UPDATE nodes SET intent = ? WHERE node_id = ?').run(spec.intent ?? null, nodeId);
|
|
92
93
|
}
|
|
94
|
+
// Wakeups cleanup — writes a DIFFERENT table (wakeups) AFTER the atomic
|
|
95
|
+
// status/intent write above, so the single-(status,intent)-writer rule holds.
|
|
96
|
+
// Event-gated; exactly ONE delegated wakeups helper per event, never inline
|
|
97
|
+
// wakeups SQL here (design §6.7 / D2 / D6, Q1/Q2):
|
|
98
|
+
// cancel → cancelWakesFor: reap node-anchored AND owner-armed detached
|
|
99
|
+
// wakes this node armed (ruling A) — covers all three cancel
|
|
100
|
+
// sites uniformly (close cascade · reapDescendants · relaunch
|
|
101
|
+
// park-old).
|
|
102
|
+
// finalize → cancelSelfAlarms: clear this node's one-shot self-alarms only;
|
|
103
|
+
// node-anchored declarative crons SURVIVE a finishing instance (Q1).
|
|
104
|
+
// crash → NOTHING. A fault is not a deliberate end-of-waiting; self-alarms
|
|
105
|
+
// and crons MUST survive instance death (D2 / Invariant E / AC-R3).
|
|
106
|
+
if (event === 'cancel')
|
|
107
|
+
cancelWakesFor(nodeId);
|
|
108
|
+
else if (event === 'finalize')
|
|
109
|
+
cancelSelfAlarms(nodeId);
|
|
93
110
|
return getNode(nodeId);
|
|
94
111
|
}
|
|
@@ -1,43 +1,3 @@
|
|
|
1
|
-
/** The node-local
|
|
2
|
-
*
|
|
3
|
-
export declare const MEMORY_TEMPLATE: string;
|
|
4
|
-
/** The user-global index template — framed around the human, not a goal. */
|
|
5
|
-
export declare const USER_MEMORY_TEMPLATE: string;
|
|
6
|
-
/** The project index template — framed around the repo. */
|
|
7
|
-
export declare const PROJECT_MEMORY_TEMPLATE: string;
|
|
8
|
-
/** The node-local memory directory in a node's context dir — holds MEMORY.md
|
|
9
|
-
* (the index) and the one-fact detail files it points at. */
|
|
1
|
+
/** The node-local memory directory in a node's context dir — holds substrate
|
|
2
|
+
* docs (.md files with kind/when/why frontmatter). */
|
|
10
3
|
export declare function memoryDir(nodeId: string): string;
|
|
11
|
-
/** The node-local MEMORY.md index path (inside the memory dir). */
|
|
12
|
-
export declare function memoryPath(nodeId: string): string;
|
|
13
|
-
/** Whether the node has a node-local memory store. This is ALSO the
|
|
14
|
-
* orchestrator gate: only orchestrators are ever seeded one, so a node with no
|
|
15
|
-
* node-local store is a terminal worker (no memory framing at all). */
|
|
16
|
-
export declare function hasMemory(nodeId: string): boolean;
|
|
17
|
-
/** Read the node-local MEMORY.md index, or null when it doesn't exist. */
|
|
18
|
-
export declare function readMemory(nodeId: string): string | null;
|
|
19
|
-
/** Seed the node-local memory dir + index IF the node has none yet. */
|
|
20
|
-
export declare function seedMemory(nodeId: string): boolean;
|
|
21
|
-
/** The user-global memory directory — one per machine, key-less, loaded into
|
|
22
|
-
* every orchestrator everywhere. */
|
|
23
|
-
export declare function userMemoryDir(): string;
|
|
24
|
-
/** The user-global MEMORY.md index path. */
|
|
25
|
-
export declare function userMemoryPath(): string;
|
|
26
|
-
export declare function hasUserMemory(): boolean;
|
|
27
|
-
/** Read the user-global MEMORY.md index, or null when it doesn't exist. */
|
|
28
|
-
export declare function readUserMemory(): string | null;
|
|
29
|
-
/** Seed the user-global memory dir + index IF absent. */
|
|
30
|
-
export declare function seedUserMemory(): boolean;
|
|
31
|
-
/** The project key for `cwd`: its git-repo-root when inside a repo, else the
|
|
32
|
-
* cwd itself, mangled into a flat directory name (reuses artifact mangleCwd).
|
|
33
|
-
* This keys the per-project memory store under <crtrHome>/projects/. */
|
|
34
|
-
export declare function projectKey(cwd: string): string;
|
|
35
|
-
/** The project memory directory for `cwd`. */
|
|
36
|
-
export declare function projectMemoryDir(cwd: string): string;
|
|
37
|
-
/** The project MEMORY.md index path for `cwd`. */
|
|
38
|
-
export declare function projectMemoryPath(cwd: string): string;
|
|
39
|
-
export declare function hasProjectMemory(cwd: string): boolean;
|
|
40
|
-
/** Read the project MEMORY.md index for `cwd`, or null when it doesn't exist. */
|
|
41
|
-
export declare function readProjectMemory(cwd: string): string | null;
|
|
42
|
-
/** Seed the project memory dir + index for `cwd` IF absent. */
|
|
43
|
-
export declare function seedProjectMemory(cwd: string): boolean;
|
|
@@ -1,165 +1,14 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// One
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// The three scopes differ only in WHERE they live and HOW LONG they outlast a
|
|
13
|
-
// node — the `type` taxonomy in each memory's frontmatter drives which store a
|
|
14
|
-
// fact lands in (the mapping lives in the orchestration kernel's "Your long-term
|
|
15
|
-
// memory"). ALL THREE live under the canvas home (crtrHome), all machine-local:
|
|
16
|
-
//
|
|
17
|
-
// user-global <crtrHome>/memory/ — who the human is, how
|
|
18
|
-
// they like to work; loaded into EVERY orchestrator everywhere.
|
|
19
|
-
// project <crtrHome>/projects/<key>/memory/ — facts bound to one
|
|
20
|
-
// repo; loaded into orchestrators whose cwd resolves to that project. <key>
|
|
21
|
-
// is the git-repo-root (walked up from the cwd), else the cwd, mangled.
|
|
22
|
-
// node-local <crtrHome>/nodes/<id>/context/memory/ — facts specific to this
|
|
23
|
-
// node's goal; dies with the node.
|
|
24
|
-
//
|
|
25
|
-
// An ORCHESTRATOR-only artifact — the resident, multi-cycle nodes that survive
|
|
26
|
-
// refreshes and accumulate durable lessons/preferences; terminal workers are
|
|
27
|
-
// one-shot and get none. All three stores are seeded the moment a node becomes
|
|
28
|
-
// an orchestrator (promotion, or born one — where the roadmap is seeded too),
|
|
29
|
-
// guarded so a re-seed never clobbers an evolved memory.
|
|
30
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
31
|
-
import { join, dirname, resolve } from 'node:path';
|
|
32
|
-
import { contextDir, crtrHome } from '../canvas/index.js';
|
|
33
|
-
import { mangleCwd } from '../artifact.js';
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
// Index template + generic store ops (shared by all three scopes).
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
/** Build the seed contents of a fresh MEMORY.md index. Deliberately tiny: the
|
|
38
|
-
* bearings block only ever extracts the pointer lines, so this prose never
|
|
39
|
-
* rides into context — it's only for a human/agent opening the file directly,
|
|
40
|
-
* and the how-to lives once in the orchestrator kernel ("Your long-term
|
|
41
|
-
* memory"), not here. `holds` is a short scope hint so the empty index still
|
|
42
|
-
* orients a fresh write. */
|
|
43
|
-
function indexTemplate(holds) {
|
|
44
|
-
return ('# memory index — one pointer line per memory (`- [Title](slug.md) — hook`); ' +
|
|
45
|
-
`how-to in "Your long-term memory". Holds ${holds}.\n\n(no memories yet)\n`);
|
|
46
|
-
}
|
|
47
|
-
/** The node-local index template. Named export kept for callers/tests that
|
|
48
|
-
* assert the seeded node store verbatim. */
|
|
49
|
-
export const MEMORY_TEMPLATE = indexTemplate('your saved memories');
|
|
50
|
-
/** The user-global index template — framed around the human, not a goal. */
|
|
51
|
-
export const USER_MEMORY_TEMPLATE = indexTemplate('your saved memories about the human — who they are and how they like to work');
|
|
52
|
-
/** The project index template — framed around the repo. */
|
|
53
|
-
export const PROJECT_MEMORY_TEMPLATE = indexTemplate('your saved memories about this project');
|
|
54
|
-
/** The MEMORY.md index path inside a memory `dir`. */
|
|
55
|
-
function indexPathOf(dir) {
|
|
56
|
-
return join(dir, 'MEMORY.md');
|
|
57
|
-
}
|
|
58
|
-
/** Seed `dir` + its MEMORY.md index with `template` IFF the index is absent.
|
|
59
|
-
* Idempotent and guarded so it never clobbers an evolved memory; creating the
|
|
60
|
-
* dir up front lets the node write detail files into it directly (no mkdir).
|
|
61
|
-
* Returns true when it seeded, false when an index already existed. */
|
|
62
|
-
function seedStore(dir, template) {
|
|
63
|
-
const idx = indexPathOf(dir);
|
|
64
|
-
if (existsSync(idx))
|
|
65
|
-
return false;
|
|
66
|
-
mkdirSync(dir, { recursive: true });
|
|
67
|
-
writeFileSync(idx, template);
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
/** Read a store's MEMORY.md index, or null when it doesn't exist. */
|
|
71
|
-
function readStore(dir) {
|
|
72
|
-
const idx = indexPathOf(dir);
|
|
73
|
-
return existsSync(idx) ? readFileSync(idx, 'utf8') : null;
|
|
74
|
-
}
|
|
75
|
-
// ---------------------------------------------------------------------------
|
|
76
|
-
// node-local store — <crtrHome>/nodes/<id>/context/memory/ (facts for this goal)
|
|
77
|
-
// ---------------------------------------------------------------------------
|
|
78
|
-
/** The node-local memory directory in a node's context dir — holds MEMORY.md
|
|
79
|
-
* (the index) and the one-fact detail files it points at. */
|
|
1
|
+
// memory.ts — the node-local substrate document store.
|
|
2
|
+
//
|
|
3
|
+
// One scope lives here: node-local memory, a `memory/` directory of substrate
|
|
4
|
+
// documents (.md files with typed frontmatter — kind, when, why,
|
|
5
|
+
// system-prompt-visibility, gate) inside a node's context dir; it dies with the
|
|
6
|
+
// node. User-global and project memory are scope-resolved stores — see
|
|
7
|
+
// `scopeMemoryDir` in core/scope.ts.
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { contextDir } from '../canvas/index.js';
|
|
10
|
+
/** The node-local memory directory in a node's context dir — holds substrate
|
|
11
|
+
* docs (.md files with kind/when/why frontmatter). */
|
|
80
12
|
export function memoryDir(nodeId) {
|
|
81
13
|
return join(contextDir(nodeId), 'memory');
|
|
82
14
|
}
|
|
83
|
-
/** The node-local MEMORY.md index path (inside the memory dir). */
|
|
84
|
-
export function memoryPath(nodeId) {
|
|
85
|
-
return indexPathOf(memoryDir(nodeId));
|
|
86
|
-
}
|
|
87
|
-
/** Whether the node has a node-local memory store. This is ALSO the
|
|
88
|
-
* orchestrator gate: only orchestrators are ever seeded one, so a node with no
|
|
89
|
-
* node-local store is a terminal worker (no memory framing at all). */
|
|
90
|
-
export function hasMemory(nodeId) {
|
|
91
|
-
return existsSync(memoryPath(nodeId));
|
|
92
|
-
}
|
|
93
|
-
/** Read the node-local MEMORY.md index, or null when it doesn't exist. */
|
|
94
|
-
export function readMemory(nodeId) {
|
|
95
|
-
return readStore(memoryDir(nodeId));
|
|
96
|
-
}
|
|
97
|
-
/** Seed the node-local memory dir + index IF the node has none yet. */
|
|
98
|
-
export function seedMemory(nodeId) {
|
|
99
|
-
return seedStore(memoryDir(nodeId), MEMORY_TEMPLATE);
|
|
100
|
-
}
|
|
101
|
-
// ---------------------------------------------------------------------------
|
|
102
|
-
// user-global store — <crtrHome>/memory/ (who the human is, how they work)
|
|
103
|
-
// ---------------------------------------------------------------------------
|
|
104
|
-
/** The user-global memory directory — one per machine, key-less, loaded into
|
|
105
|
-
* every orchestrator everywhere. */
|
|
106
|
-
export function userMemoryDir() {
|
|
107
|
-
return join(crtrHome(), 'memory');
|
|
108
|
-
}
|
|
109
|
-
/** The user-global MEMORY.md index path. */
|
|
110
|
-
export function userMemoryPath() {
|
|
111
|
-
return indexPathOf(userMemoryDir());
|
|
112
|
-
}
|
|
113
|
-
export function hasUserMemory() {
|
|
114
|
-
return existsSync(userMemoryPath());
|
|
115
|
-
}
|
|
116
|
-
/** Read the user-global MEMORY.md index, or null when it doesn't exist. */
|
|
117
|
-
export function readUserMemory() {
|
|
118
|
-
return readStore(userMemoryDir());
|
|
119
|
-
}
|
|
120
|
-
/** Seed the user-global memory dir + index IF absent. */
|
|
121
|
-
export function seedUserMemory() {
|
|
122
|
-
return seedStore(userMemoryDir(), USER_MEMORY_TEMPLATE);
|
|
123
|
-
}
|
|
124
|
-
// ---------------------------------------------------------------------------
|
|
125
|
-
// project store — <crtrHome>/projects/<key>/memory/ (facts bound to one repo)
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
|
-
/** The git repo root containing `cwd` — walk up for a `.git` entry — or null
|
|
128
|
-
* when `cwd` is not inside a repo. `.git` may be a dir (normal) or a file
|
|
129
|
-
* (worktree/submodule); existsSync catches both. */
|
|
130
|
-
function gitRoot(cwd) {
|
|
131
|
-
let dir = resolve(cwd);
|
|
132
|
-
for (;;) {
|
|
133
|
-
if (existsSync(join(dir, '.git')))
|
|
134
|
-
return dir;
|
|
135
|
-
const parent = dirname(dir);
|
|
136
|
-
if (parent === dir)
|
|
137
|
-
return null; // hit the filesystem root
|
|
138
|
-
dir = parent;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
/** The project key for `cwd`: its git-repo-root when inside a repo, else the
|
|
142
|
-
* cwd itself, mangled into a flat directory name (reuses artifact mangleCwd).
|
|
143
|
-
* This keys the per-project memory store under <crtrHome>/projects/. */
|
|
144
|
-
export function projectKey(cwd) {
|
|
145
|
-
return mangleCwd(gitRoot(cwd) ?? cwd);
|
|
146
|
-
}
|
|
147
|
-
/** The project memory directory for `cwd`. */
|
|
148
|
-
export function projectMemoryDir(cwd) {
|
|
149
|
-
return join(crtrHome(), 'projects', projectKey(cwd), 'memory');
|
|
150
|
-
}
|
|
151
|
-
/** The project MEMORY.md index path for `cwd`. */
|
|
152
|
-
export function projectMemoryPath(cwd) {
|
|
153
|
-
return indexPathOf(projectMemoryDir(cwd));
|
|
154
|
-
}
|
|
155
|
-
export function hasProjectMemory(cwd) {
|
|
156
|
-
return existsSync(projectMemoryPath(cwd));
|
|
157
|
-
}
|
|
158
|
-
/** Read the project MEMORY.md index for `cwd`, or null when it doesn't exist. */
|
|
159
|
-
export function readProjectMemory(cwd) {
|
|
160
|
-
return readStore(projectMemoryDir(cwd));
|
|
161
|
-
}
|
|
162
|
-
/** Seed the project memory dir + index for `cwd` IF absent. */
|
|
163
|
-
export function seedProjectMemory(cwd) {
|
|
164
|
-
return seedStore(projectMemoryDir(cwd), PROJECT_MEMORY_TEMPLATE);
|
|
165
|
-
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { type NodeMeta, type Mode, type Lifecycle, type LaunchSpec } from '../canvas/index.js';
|
|
2
2
|
/** Generate a node id in the same shape as job ids (time-sortable + random). */
|
|
3
3
|
export declare function newNodeId(): string;
|
|
4
|
+
/** The root of a node's spine: walk the `parent` column up to `parent == null`.
|
|
5
|
+
* Cycle-guarded (parents must not cycle, but never loop forever). Lives in the
|
|
6
|
+
* node layer (not placement) so the env-builder and placement both reach it
|
|
7
|
+
* without an import cycle — `CRTR_SUBTREE` is emitted as `rootOfSpine(nodeId)`
|
|
8
|
+
* at every launch site. */
|
|
9
|
+
export declare function rootOfSpine(nodeId: string): string;
|
|
4
10
|
/** The single, shared tmux session that ALL canvas node windows live in.
|
|
5
11
|
* Overridable with CRTR_NODE_SESSION (default `crtr`). Every root and every
|
|
6
12
|
* child opens a window here rather than cluttering the user's own working
|
|
@@ -34,6 +40,25 @@ export declare function resolveBirthSession(opts: {
|
|
|
34
40
|
* (`nodeSession()`). The defaulted read for the placement layer; a present
|
|
35
41
|
* `home_session` is always returned verbatim. */
|
|
36
42
|
export declare function homeSessionOf(nodeId: string): string;
|
|
43
|
+
/** The session a node's CHILDREN spawn into — the value its pi process exports
|
|
44
|
+
* as CRTR_ROOT_SESSION (the subtree's backstage). NOT the same as where the
|
|
45
|
+
* node's OWN pane lives.
|
|
46
|
+
*
|
|
47
|
+
* For a managed CHILD this is its `home_session`: a child's home_session is
|
|
48
|
+
* ALWAYS the backstage it was born into (focus may move its live pane into a
|
|
49
|
+
* user session, but home_session stays the taint-immune backstage), so its
|
|
50
|
+
* descendants correctly inherit that backstage.
|
|
51
|
+
*
|
|
52
|
+
* For a ROOT (`parent === null`) `home_session` may be a USER session it
|
|
53
|
+
* ADOPTED — the inline front door keeps the root's own pane where the user is
|
|
54
|
+
* working (`home_session = cli`). Sourcing children's CRTR_ROOT_SESSION from
|
|
55
|
+
* that would exile the root's entire subtree into the user's session. A root's
|
|
56
|
+
* children must instead flow to the shared backstage `nodeSession()`, NEVER the
|
|
57
|
+
* user's session — mirroring spawnRoot's birth-time split (root pane → user
|
|
58
|
+
* session; children → nodeSession() via CRTR_ROOT_SESSION). The revive paths
|
|
59
|
+
* MUST honor the same split, or a refreshed front-door root re-points its whole
|
|
60
|
+
* subtree into the user's session on every yield (the bug this fixes). */
|
|
61
|
+
export declare function childBackstageOf(nodeId: string): string;
|
|
37
62
|
export interface NodeContext {
|
|
38
63
|
nodeId: string | null;
|
|
39
64
|
kind: string | null;
|
|
@@ -61,10 +86,22 @@ export interface SpawnNodeOpts {
|
|
|
61
86
|
* `parent` — e.g. an independent root (parent=null) still records its
|
|
62
87
|
* spawner. Defaults to `parent`. */
|
|
63
88
|
spawnedBy?: string | null;
|
|
89
|
+
/** Fork provenance — the `--fork-from` reference this node was forked from (a
|
|
90
|
+
* node id / session path / pi session uuid). Persisted to `meta.fork_from` so
|
|
91
|
+
* the boot intro can re-assert this node's OWN identity over the source's
|
|
92
|
+
* copied-in conversation. Omit for a fresh node. */
|
|
93
|
+
forkFrom?: string | null;
|
|
94
|
+
/** Which HOST launches + supervises this node: a tmux pane (default) or the
|
|
95
|
+
* headless broker. Persisted to `meta.host_kind` (NULL/'tmux' ⇒ tmux). */
|
|
96
|
+
hostKind?: 'tmux' | 'broker';
|
|
64
97
|
/** New subscriptions this node opens default to passive when true. */
|
|
65
98
|
passiveDefault?: boolean;
|
|
66
99
|
/** Resolved pi launch recipe (from resolve(kind,mode)). */
|
|
67
100
|
launch?: LaunchSpec;
|
|
101
|
+
/** Caller-pinned model tier (ultra/strong/medium/light) that overrides the
|
|
102
|
+
* persona default. Persisted to `meta.model_override` so polymorphs preserve
|
|
103
|
+
* it. Omit to use the persona default. */
|
|
104
|
+
modelOverride?: string | null;
|
|
68
105
|
/** Override the generated id (e.g. when a caller pre-allocates one). */
|
|
69
106
|
nodeId?: string;
|
|
70
107
|
}
|
|
@@ -13,11 +13,31 @@
|
|
|
13
13
|
// graph to mirror the spawn structure. A `spawned_by` audit edge
|
|
14
14
|
// is also recorded.
|
|
15
15
|
import { randomBytes } from 'node:crypto';
|
|
16
|
-
import {
|
|
16
|
+
import { mkdirSync } from 'node:fs';
|
|
17
|
+
import { createNode, getNode, getRow, subscribe, recordSpawn, } from '../canvas/index.js';
|
|
18
|
+
import { memoryDir } from './memory.js';
|
|
17
19
|
/** Generate a node id in the same shape as job ids (time-sortable + random). */
|
|
18
20
|
export function newNodeId() {
|
|
19
21
|
return `${Date.now().toString(36)}-${randomBytes(4).toString('hex')}`;
|
|
20
22
|
}
|
|
23
|
+
/** The root of a node's spine: walk the `parent` column up to `parent == null`.
|
|
24
|
+
* Cycle-guarded (parents must not cycle, but never loop forever). Lives in the
|
|
25
|
+
* node layer (not placement) so the env-builder and placement both reach it
|
|
26
|
+
* without an import cycle — `CRTR_SUBTREE` is emitted as `rootOfSpine(nodeId)`
|
|
27
|
+
* at every launch site. */
|
|
28
|
+
export function rootOfSpine(nodeId) {
|
|
29
|
+
let cur = nodeId;
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
for (;;) {
|
|
32
|
+
if (seen.has(cur))
|
|
33
|
+
return cur;
|
|
34
|
+
seen.add(cur);
|
|
35
|
+
const row = getRow(cur);
|
|
36
|
+
if (row === null || row.parent == null)
|
|
37
|
+
return cur;
|
|
38
|
+
cur = row.parent;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
21
41
|
/** The single, shared tmux session that ALL canvas node windows live in.
|
|
22
42
|
* Overridable with CRTR_NODE_SESSION (default `crtr`). Every root and every
|
|
23
43
|
* child opens a window here rather than cluttering the user's own working
|
|
@@ -59,6 +79,30 @@ export function homeSessionOf(nodeId) {
|
|
|
59
79
|
return nodeSession();
|
|
60
80
|
return meta.home_session ?? meta.tmux_session ?? nodeSession();
|
|
61
81
|
}
|
|
82
|
+
/** The session a node's CHILDREN spawn into — the value its pi process exports
|
|
83
|
+
* as CRTR_ROOT_SESSION (the subtree's backstage). NOT the same as where the
|
|
84
|
+
* node's OWN pane lives.
|
|
85
|
+
*
|
|
86
|
+
* For a managed CHILD this is its `home_session`: a child's home_session is
|
|
87
|
+
* ALWAYS the backstage it was born into (focus may move its live pane into a
|
|
88
|
+
* user session, but home_session stays the taint-immune backstage), so its
|
|
89
|
+
* descendants correctly inherit that backstage.
|
|
90
|
+
*
|
|
91
|
+
* For a ROOT (`parent === null`) `home_session` may be a USER session it
|
|
92
|
+
* ADOPTED — the inline front door keeps the root's own pane where the user is
|
|
93
|
+
* working (`home_session = cli`). Sourcing children's CRTR_ROOT_SESSION from
|
|
94
|
+
* that would exile the root's entire subtree into the user's session. A root's
|
|
95
|
+
* children must instead flow to the shared backstage `nodeSession()`, NEVER the
|
|
96
|
+
* user's session — mirroring spawnRoot's birth-time split (root pane → user
|
|
97
|
+
* session; children → nodeSession() via CRTR_ROOT_SESSION). The revive paths
|
|
98
|
+
* MUST honor the same split, or a refreshed front-door root re-points its whole
|
|
99
|
+
* subtree into the user's session on every yield (the bug this fixes). */
|
|
100
|
+
export function childBackstageOf(nodeId) {
|
|
101
|
+
const meta = getNode(nodeId);
|
|
102
|
+
if (meta !== null && meta.parent === null)
|
|
103
|
+
return nodeSession();
|
|
104
|
+
return homeSessionOf(nodeId);
|
|
105
|
+
}
|
|
62
106
|
/** Read the current node's identity from the environment. A spawned pi process
|
|
63
107
|
* runs with CRTR_NODE_ID set; its own `crtr` invocations spawn children under
|
|
64
108
|
* it by reading CRTR_NODE_ID as the parent. */
|
|
@@ -92,6 +136,12 @@ export function nodeEnv(meta) {
|
|
|
92
136
|
const rootSession = process.env['CRTR_ROOT_SESSION'];
|
|
93
137
|
if (rootSession !== undefined && rootSession !== '')
|
|
94
138
|
env['CRTR_ROOT_SESSION'] = rootSession;
|
|
139
|
+
// Propagate the subtree-root node id (additive in Phase 3; tmux still keys on
|
|
140
|
+
// CRTR_ROOT_SESSION). The broker uses it to group a subtree; each launch site
|
|
141
|
+
// also emits the authoritative rootOfSpine(nodeId) over this passthrough.
|
|
142
|
+
const subtree = process.env['CRTR_SUBTREE'];
|
|
143
|
+
if (subtree !== undefined && subtree !== '')
|
|
144
|
+
env['CRTR_SUBTREE'] = subtree;
|
|
95
145
|
// Merge any launch-spec env last (it may override / extend).
|
|
96
146
|
return { ...env, ...(meta.launch?.env ?? {}) };
|
|
97
147
|
}
|
|
@@ -117,6 +167,7 @@ export function spawnNode(opts) {
|
|
|
117
167
|
cycles: 0,
|
|
118
168
|
created: new Date().toISOString(),
|
|
119
169
|
cwd: opts.cwd,
|
|
170
|
+
host_kind: opts.hostKind ?? 'tmux',
|
|
120
171
|
kind: opts.kind,
|
|
121
172
|
mode,
|
|
122
173
|
lifecycle,
|
|
@@ -127,6 +178,8 @@ export function spawnNode(opts) {
|
|
|
127
178
|
status: 'active',
|
|
128
179
|
parent,
|
|
129
180
|
spawned_by: spawnedBy,
|
|
181
|
+
fork_from: opts.forkFrom,
|
|
182
|
+
model_override: opts.modelOverride ?? null,
|
|
130
183
|
passive_default: opts.passiveDefault ?? false,
|
|
131
184
|
intent: null,
|
|
132
185
|
pi_session_id: null,
|
|
@@ -140,6 +193,12 @@ export function spawnNode(opts) {
|
|
|
140
193
|
throw new Error(`cannot spawn under unknown parent node: ${parent}`);
|
|
141
194
|
}
|
|
142
195
|
createNode(meta);
|
|
196
|
+
// Create the node-local memory directory so substrate docs can be written
|
|
197
|
+
// directly into it without a separate mkdir. Skip for ephemeral human-bridge
|
|
198
|
+
// rows (kind 'human') — they are not agent nodes.
|
|
199
|
+
if (meta.kind !== 'human') {
|
|
200
|
+
mkdirSync(memoryDir(meta.node_id), { recursive: true });
|
|
201
|
+
}
|
|
143
202
|
if (parent !== null) {
|
|
144
203
|
// The load-bearing seed: parent subscribes (active) to child so it learns
|
|
145
204
|
// when the work finishes. This mirrors spawn structure into the spine.
|
|
@@ -26,11 +26,22 @@ import { readText } from '../fs-utils.js';
|
|
|
26
26
|
import { parseFrontmatter } from '../frontmatter.js';
|
|
27
27
|
import { readRoadmap, roadmapPath } from './roadmap.js';
|
|
28
28
|
import { orchestratorContextNote } from './bearings.js';
|
|
29
|
-
import { memoryPath, memoryDir, userMemoryPath, userMemoryDir, projectMemoryPath, projectMemoryDir, } from './memory.js';
|
|
30
29
|
// ---------------------------------------------------------------------------
|
|
31
30
|
// base→orchestrator guidance (the roadmap-shaping dump) — MOVED here from
|
|
32
31
|
// promote.ts so the injector is the one place that builds it.
|
|
33
32
|
// ---------------------------------------------------------------------------
|
|
33
|
+
/** Coerce a frontmatter scalar to its string form, matching the legacy
|
|
34
|
+
* hand-rolled parser. Strings pass through; number/boolean coerce via
|
|
35
|
+
* String(); null/undefined and non-scalars are dropped (→ null). Without this,
|
|
36
|
+
* the `yaml` package's native scalar types would let a numeric/boolean
|
|
37
|
+
* `roadmapSkill` be silently dropped by a `typeof === 'string'` guard. */
|
|
38
|
+
function scalarToString(v) {
|
|
39
|
+
if (typeof v === 'string')
|
|
40
|
+
return v;
|
|
41
|
+
if (typeof v === 'number' || typeof v === 'boolean')
|
|
42
|
+
return String(v);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
34
45
|
/** Load a skill's body text by name, or null if it can't be resolved. Used to
|
|
35
46
|
* inline a kind's roadmap-shaping skill into the orchestration guidance. */
|
|
36
47
|
function loadSkillBody(name) {
|
|
@@ -44,16 +55,15 @@ function loadSkillBody(name) {
|
|
|
44
55
|
}
|
|
45
56
|
/** The base→orchestrator guidance dump, specialized to the node's kind: the
|
|
46
57
|
* shared kernel + that kind's roadmap-shaping skill + the roadmap scaffold the
|
|
47
|
-
* node must author + the orchestrator context-dir framing +
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
|
|
58
|
+
* node must author + the orchestrator context-dir framing + a short pointer at
|
|
59
|
+
* the substrate memory flow (promotion re-informs of nothing about the stores,
|
|
60
|
+
* it only adds the across-cycles relevance). The node is now a delegator whose
|
|
61
|
+
* scarce resource is its own context window. (Lifecycle is left to its own
|
|
62
|
+
* section — promotion no longer forces resident, so this never asserts residency.) */
|
|
63
|
+
function orchestrationGuidance(nodeId, kind) {
|
|
52
64
|
const kernel = loadKernel();
|
|
53
65
|
const orch = loadPersona(kind, 'orchestrator');
|
|
54
|
-
const roadmapSkill =
|
|
55
|
-
? orch.frontmatter['roadmapSkill']
|
|
56
|
-
: undefined;
|
|
66
|
+
const roadmapSkill = scalarToString(orch?.frontmatter?.['roadmapSkill']) ?? undefined;
|
|
57
67
|
const skillBody = roadmapSkill ? loadSkillBody(roadmapSkill) : null;
|
|
58
68
|
const roadmap = readRoadmap(nodeId) ?? '(no roadmap yet)';
|
|
59
69
|
const rmPath = roadmapPath(nodeId);
|
|
@@ -70,7 +80,7 @@ function orchestrationGuidance(nodeId, kind, cwd) {
|
|
|
70
80
|
// The orchestrator framing for the context dir — the missing guidance a
|
|
71
81
|
// promoted node never got at spawn (it spawned as a base worker). Same note
|
|
72
82
|
// a born-orchestrator gets in its <crtr-context> bearings block.
|
|
73
|
-
orchestratorContextNote(nodeId), '', 'Your long-term memory
|
|
83
|
+
orchestratorContextNote(nodeId), '', 'Your three long-term memory scopes (user-global, project, node-local) already surface in your `<crtr-context>` block. What promotion changes is USE, not access: you now persist across refresh cycles, so node-local carries what THIS goal needs between your refreshes, and durable facts you commit to user-global or project outlive this node. Use `crtr memory write` to save a document; `crtr memory list` to browse; `crtr memory find` to search (see "Your long-term memory" above for the full substrate flow).', '', 'Then delegate each phase with `crtr node new --kind <kind>`. When your context fills, run `crtr node yield` to refresh against this roadmap.');
|
|
74
84
|
return parts.join('\n');
|
|
75
85
|
}
|
|
76
86
|
// ---------------------------------------------------------------------------
|
|
@@ -103,7 +113,7 @@ export function transitionGuidance(nodeId, from, to) {
|
|
|
103
113
|
if (from.mode !== to.mode) {
|
|
104
114
|
if (to.mode === 'orchestrator') {
|
|
105
115
|
const node = getNode(nodeId);
|
|
106
|
-
sections.push(orchestrationGuidance(nodeId, node?.kind ?? 'general'
|
|
116
|
+
sections.push(orchestrationGuidance(nodeId, node?.kind ?? 'general'));
|
|
107
117
|
}
|
|
108
118
|
else {
|
|
109
119
|
sections.push(baseModeGuidance());
|