@crouton-kit/crouter 0.3.14 → 0.3.16
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.d.ts +8 -0
- package/dist/build-root.js +30 -0
- package/dist/builtin-personas/design/base.md +3 -7
- package/dist/builtin-personas/design/orchestrator.md +4 -3
- package/dist/builtin-personas/developer/base.md +3 -7
- package/dist/builtin-personas/developer/orchestrator.md +5 -4
- package/dist/builtin-personas/explore/base.md +3 -7
- package/dist/builtin-personas/explore/orchestrator.md +1 -5
- package/dist/builtin-personas/general/base.md +2 -4
- package/dist/builtin-personas/general/orchestrator.md +2 -4
- package/dist/builtin-personas/lifecycle/resident.md +2 -0
- package/dist/builtin-personas/lifecycle/terminal.md +6 -0
- package/dist/builtin-personas/orchestration-kernel.md +42 -3
- package/dist/builtin-personas/plan/base.md +3 -5
- package/dist/builtin-personas/plan/orchestrator.md +5 -4
- package/dist/builtin-personas/plan/reviewers/architecture-fit/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/code-smells/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/base.md +9 -0
- package/dist/builtin-personas/plan/reviewers/security/base.md +9 -0
- package/dist/builtin-personas/review/base.md +3 -5
- package/dist/builtin-personas/review/orchestrator.md +2 -6
- package/dist/builtin-personas/runtime-base.md +3 -19
- package/dist/builtin-personas/spec/base.md +3 -5
- package/dist/builtin-personas/spec/orchestrator.md +4 -3
- package/dist/builtin-personas/spine/has-manager.md +10 -0
- package/dist/builtin-personas/spine/no-manager.md +2 -0
- package/dist/builtin-skills/skills/crouter-development/personas/SKILL.md +96 -0
- package/dist/builtin-skills/skills/crouter-development/personas/base-prompt/SKILL.md +49 -0
- package/dist/builtin-skills/skills/crouter-development/personas/orchestrator-prompt/SKILL.md +49 -0
- package/dist/builtin-skills/skills/planning/SKILL.md +1 -1
- package/dist/builtin-skills/skills/spec/SKILL.md +2 -2
- package/dist/cli.js +6 -29
- package/dist/commands/attention.js +76 -7
- package/dist/commands/canvas-prune.d.ts +2 -0
- package/dist/commands/canvas-prune.js +66 -0
- package/dist/commands/canvas.js +5 -8
- package/dist/commands/chord.d.ts +2 -0
- package/dist/commands/chord.js +143 -0
- package/dist/commands/daemon.js +8 -5
- package/dist/commands/dashboard.js +2 -0
- package/dist/commands/human/prompts.js +28 -27
- package/dist/commands/human/queue.js +30 -14
- package/dist/commands/human/shared.d.ts +26 -21
- package/dist/commands/human/shared.js +45 -67
- package/dist/commands/human.js +4 -14
- package/dist/commands/node.d.ts +11 -0
- package/dist/commands/node.js +221 -99
- package/dist/commands/pkg/market-inspect.js +6 -4
- package/dist/commands/pkg/market-manage.js +10 -6
- package/dist/commands/pkg/market.js +2 -4
- package/dist/commands/pkg/plugin-inspect.js +6 -4
- package/dist/commands/pkg/plugin-manage.js +12 -7
- package/dist/commands/pkg/plugin.js +2 -4
- package/dist/commands/pkg.js +0 -4
- package/dist/commands/push.js +178 -15
- package/dist/commands/revive.js +5 -3
- package/dist/commands/skill/author.js +6 -4
- package/dist/commands/skill/find.js +8 -5
- package/dist/commands/skill/read.js +2 -0
- package/dist/commands/skill/state.js +6 -4
- package/dist/commands/skill.js +0 -6
- package/dist/commands/sys/config.js +21 -7
- package/dist/commands/sys/doctor.js +2 -0
- package/dist/commands/sys/update.js +4 -0
- package/dist/commands/sys.js +0 -6
- package/dist/commands/tmux-spread.d.ts +2 -0
- package/dist/commands/tmux-spread.js +129 -0
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +25 -0
- package/dist/core/__tests__/child-followup.test.d.ts +1 -0
- package/dist/core/__tests__/child-followup.test.js +83 -0
- package/dist/core/__tests__/close.test.d.ts +1 -0
- package/dist/core/__tests__/close.test.js +148 -0
- package/dist/core/__tests__/context-intro.test.d.ts +1 -0
- package/dist/core/__tests__/context-intro.test.js +196 -0
- package/dist/core/__tests__/daemon-boot.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-boot.test.js +93 -0
- package/dist/core/__tests__/daemon-liveness.test.d.ts +1 -0
- package/dist/core/__tests__/daemon-liveness.test.js +223 -0
- package/dist/core/__tests__/focuses.test.d.ts +1 -0
- package/dist/core/__tests__/focuses.test.js +196 -0
- package/dist/core/__tests__/fork.test.d.ts +1 -0
- package/dist/core/__tests__/fork.test.js +91 -0
- package/dist/core/__tests__/home-session.test.d.ts +1 -0
- package/dist/core/__tests__/home-session.test.js +153 -0
- package/dist/core/__tests__/human-cancel-guard.test.d.ts +1 -0
- package/dist/core/__tests__/human-cancel-guard.test.js +49 -0
- package/dist/core/__tests__/keystone.test.d.ts +1 -0
- package/dist/core/__tests__/keystone.test.js +185 -0
- package/dist/core/__tests__/kickoff.test.d.ts +1 -0
- package/dist/core/__tests__/kickoff.test.js +89 -0
- package/dist/core/__tests__/lifecycle.test.d.ts +1 -0
- package/dist/core/__tests__/lifecycle.test.js +178 -0
- package/dist/core/__tests__/listing-completeness.test.d.ts +1 -0
- package/dist/core/__tests__/listing-completeness.test.js +31 -0
- package/dist/core/__tests__/memory.test.d.ts +1 -0
- package/dist/core/__tests__/memory.test.js +152 -0
- package/dist/core/__tests__/migration.test.d.ts +1 -0
- package/dist/core/__tests__/migration.test.js +238 -0
- package/dist/core/__tests__/pane-column.test.d.ts +1 -0
- package/dist/core/__tests__/pane-column.test.js +153 -0
- package/dist/core/__tests__/passive-subscription.test.js +24 -1
- package/dist/core/__tests__/persona-compose.test.d.ts +1 -0
- package/dist/core/__tests__/persona-compose.test.js +53 -0
- package/dist/core/__tests__/persona-subkind.test.d.ts +1 -0
- package/dist/core/__tests__/persona-subkind.test.js +62 -0
- package/dist/core/__tests__/persona.test.d.ts +1 -0
- package/dist/core/__tests__/persona.test.js +107 -0
- package/dist/core/__tests__/placement-focus.test.d.ts +1 -0
- package/dist/core/__tests__/placement-focus.test.js +266 -0
- package/dist/core/__tests__/placement-reconcile.test.d.ts +1 -0
- package/dist/core/__tests__/placement-reconcile.test.js +212 -0
- package/dist/core/__tests__/placement-revive.test.d.ts +1 -0
- package/dist/core/__tests__/placement-revive.test.js +238 -0
- package/dist/core/__tests__/placement-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/placement-teardown.test.js +178 -0
- package/dist/core/__tests__/prune.test.d.ts +1 -0
- package/dist/core/__tests__/prune.test.js +116 -0
- package/dist/core/__tests__/push-final-guard.test.d.ts +1 -0
- package/dist/core/__tests__/push-final-guard.test.js +71 -0
- package/dist/core/__tests__/relaunch.test.d.ts +1 -0
- package/dist/core/__tests__/relaunch.test.js +334 -0
- package/dist/core/__tests__/reset.test.js +26 -7
- package/dist/core/__tests__/revive.test.d.ts +1 -0
- package/dist/core/__tests__/revive.test.js +217 -0
- package/dist/core/__tests__/spawn-root.test.d.ts +1 -0
- package/dist/core/__tests__/spawn-root.test.js +73 -0
- package/dist/core/__tests__/steer-note.test.d.ts +1 -0
- package/dist/core/__tests__/steer-note.test.js +39 -0
- package/dist/core/__tests__/stop-guard.test.d.ts +1 -0
- package/dist/core/__tests__/stop-guard.test.js +82 -0
- package/dist/core/__tests__/subcommand-tier.test.js +35 -33
- package/dist/core/__tests__/tmux-surface.test.d.ts +1 -0
- package/dist/core/__tests__/tmux-surface.test.js +105 -0
- package/dist/core/__tests__/unknown-path.test.js +8 -2
- package/dist/core/canvas/attention.d.ts +10 -0
- package/dist/core/canvas/attention.js +40 -0
- package/dist/core/canvas/canvas.d.ts +66 -7
- package/dist/core/canvas/canvas.js +209 -21
- package/dist/core/canvas/db.d.ts +8 -0
- package/dist/core/canvas/db.js +205 -4
- package/dist/core/canvas/focuses.d.ts +22 -0
- package/dist/core/canvas/focuses.js +81 -0
- package/dist/core/canvas/index.d.ts +3 -0
- package/dist/core/canvas/index.js +3 -0
- package/dist/core/canvas/labels.d.ts +27 -0
- package/dist/core/canvas/labels.js +36 -0
- package/dist/core/canvas/render.js +25 -10
- package/dist/core/canvas/telemetry.d.ts +14 -0
- package/dist/core/canvas/telemetry.js +35 -0
- package/dist/core/canvas/types.d.ts +115 -12
- package/dist/core/command.d.ts +25 -1
- package/dist/core/command.js +23 -15
- package/dist/core/config.js +36 -2
- package/dist/core/feed/feed.js +3 -3
- package/dist/core/feed/inbox.d.ts +3 -1
- package/dist/core/feed/inbox.js +45 -5
- package/dist/core/feed/passive.js +24 -11
- package/dist/core/help.d.ts +26 -13
- package/dist/core/help.js +44 -37
- package/dist/core/personas/index.d.ts +1 -1
- package/dist/core/personas/index.js +1 -1
- package/dist/core/personas/loader.d.ts +40 -1
- package/dist/core/personas/loader.js +63 -1
- package/dist/core/personas/resolve.d.ts +13 -6
- package/dist/core/personas/resolve.js +46 -34
- package/dist/core/runtime/bearings.d.ts +20 -0
- package/dist/core/runtime/bearings.js +92 -0
- package/dist/core/runtime/close.d.ts +14 -0
- package/dist/core/runtime/close.js +151 -0
- package/dist/core/runtime/demote.js +24 -12
- package/dist/core/runtime/front-door.js +1 -1
- package/dist/core/runtime/kickoff.d.ts +23 -6
- package/dist/core/runtime/kickoff.js +92 -36
- package/dist/core/runtime/launch.d.ts +26 -12
- package/dist/core/runtime/launch.js +78 -19
- package/dist/core/runtime/lifecycle.d.ts +13 -0
- package/dist/core/runtime/lifecycle.js +86 -0
- package/dist/core/runtime/memory.d.ts +43 -0
- package/dist/core/runtime/memory.js +165 -0
- package/dist/core/runtime/naming.d.ts +22 -0
- package/dist/core/runtime/naming.js +166 -0
- package/dist/core/runtime/nodes.d.ts +39 -1
- package/dist/core/runtime/nodes.js +69 -10
- package/dist/core/runtime/persona.d.ts +25 -0
- package/dist/core/runtime/persona.js +139 -0
- package/dist/core/runtime/placement.d.ts +299 -0
- package/dist/core/runtime/placement.js +688 -0
- package/dist/core/runtime/promote.d.ts +14 -7
- package/dist/core/runtime/promote.js +57 -67
- package/dist/core/runtime/reset.d.ts +47 -4
- package/dist/core/runtime/reset.js +223 -52
- package/dist/core/runtime/revive.d.ts +26 -2
- package/dist/core/runtime/revive.js +166 -39
- package/dist/core/runtime/spawn.d.ts +20 -5
- package/dist/core/runtime/spawn.js +163 -43
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +18 -8
- package/dist/core/runtime/tmux-chrome.d.ts +1 -0
- package/dist/core/runtime/tmux-chrome.js +4 -0
- package/dist/core/runtime/tmux.d.ts +113 -20
- package/dist/core/runtime/tmux.js +221 -39
- package/dist/core/spawn.js +15 -0
- package/dist/daemon/crtrd.d.ts +12 -1
- package/dist/daemon/crtrd.js +152 -34
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +266 -0
- package/dist/pi-extensions/canvas-commands.js +16 -13
- package/dist/pi-extensions/canvas-context-intro.d.ts +70 -0
- package/dist/pi-extensions/canvas-context-intro.js +164 -0
- package/dist/pi-extensions/canvas-goal-capture.d.ts +3 -0
- package/dist/pi-extensions/canvas-goal-capture.js +15 -1
- package/dist/pi-extensions/canvas-inbox-watcher.js +11 -0
- package/dist/pi-extensions/canvas-nav.d.ts +12 -4
- package/dist/pi-extensions/canvas-nav.js +594 -262
- package/dist/pi-extensions/canvas-resume.d.ts +22 -0
- package/dist/pi-extensions/canvas-resume.js +173 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +16 -0
- package/dist/pi-extensions/canvas-stophook.js +340 -228
- package/dist/types.d.ts +28 -0
- package/dist/types.js +16 -0
- package/package.json +2 -2
- package/dist/core/runtime/presence.d.ts +0 -38
- package/dist/core/runtime/presence.js +0 -154
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
//
|
|
3
3
|
// pi-only. No claude branch — we are a super-opinionated system. A node's
|
|
4
4
|
// LaunchSpec (persisted in meta.json) is the canonical recipe the daemon
|
|
5
|
-
// replays to revive it faithfully: `--
|
|
6
|
-
// its conversation), or fresh (against the context dir) for a refresh-yield.
|
|
5
|
+
// replays to revive it faithfully: `--session <id>` to wake a done/idle node
|
|
6
|
+
// (keeps its conversation), or fresh (against the context dir) for a refresh-yield.
|
|
7
7
|
// The spec is rewritten on every polymorph (base→orchestrator) so a node
|
|
8
8
|
// always comes back as its *current* self.
|
|
9
|
-
import { existsSync } from 'node:fs';
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
10
10
|
import { dirname, join } from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { resolve as resolvePersona } from '../personas/index.js';
|
|
13
13
|
import { nodeEnv } from './nodes.js';
|
|
14
|
+
import { editorLabel } from '../canvas/index.js';
|
|
15
|
+
import { nodeDir } from '../canvas/paths.js';
|
|
14
16
|
// ---------------------------------------------------------------------------
|
|
15
17
|
// The two canvas pi-extensions every node loads. They self-gate on the live
|
|
16
18
|
// {kind,mode} env, so the worker→orchestrator polymorph flips hook behavior
|
|
@@ -29,20 +31,27 @@ export const CANVAS_INBOX_WATCHER_PATH = resolveExtension('canvas-inbox-watcher'
|
|
|
29
31
|
export const CANVAS_NAV_PATH = resolveExtension('canvas-nav');
|
|
30
32
|
export const CANVAS_GOAL_CAPTURE_PATH = resolveExtension('canvas-goal-capture');
|
|
31
33
|
export const CANVAS_PASSIVE_CONTEXT_PATH = resolveExtension('canvas-passive-context');
|
|
34
|
+
export const CANVAS_CONTEXT_INTRO_PATH = resolveExtension('canvas-context-intro');
|
|
32
35
|
export const CANVAS_COMMANDS_PATH = resolveExtension('canvas-commands');
|
|
36
|
+
export const CANVAS_RESUME_PATH = resolveExtension('canvas-resume');
|
|
33
37
|
/** The canvas extensions every node loads, in order: stophook (routing +
|
|
34
38
|
* telemetry + session-id capture), inbox-watcher (wake), nav (in-editor
|
|
35
39
|
* graph chrome), goal-capture (persist the first user message as the goal),
|
|
36
40
|
* passive-context (drain passive backlog as pre-text on the next message),
|
|
37
|
-
*
|
|
38
|
-
*
|
|
41
|
+
* context-intro (inject the <crtr-context> bearings block as its own session
|
|
42
|
+
* message, once per brand-new chat), commands (the /promote slash-command),
|
|
43
|
+
* resume (the /resume-node whole-canvas picker → `crtr node focus`).
|
|
44
|
+
* All self-gate on CRTR_NODE_ID. goal-capture precedes passive-context so it
|
|
45
|
+
* reads the raw user text. */
|
|
39
46
|
export const CANVAS_EXTENSIONS = [
|
|
40
47
|
CANVAS_STOPHOOK_PATH,
|
|
41
48
|
CANVAS_INBOX_WATCHER_PATH,
|
|
42
49
|
CANVAS_NAV_PATH,
|
|
43
50
|
CANVAS_GOAL_CAPTURE_PATH,
|
|
44
51
|
CANVAS_PASSIVE_CONTEXT_PATH,
|
|
52
|
+
CANVAS_CONTEXT_INTRO_PATH,
|
|
45
53
|
CANVAS_COMMANDS_PATH,
|
|
54
|
+
CANVAS_RESUME_PATH,
|
|
46
55
|
];
|
|
47
56
|
/** Bare model aliases resolve to the anthropic provider under pi (avoids the
|
|
48
57
|
* bedrock default). Anything with a `/` or an unknown name passes through. */
|
|
@@ -55,10 +64,15 @@ export function normalizeModel(model) {
|
|
|
55
64
|
// ---------------------------------------------------------------------------
|
|
56
65
|
// Build the launch spec from {kind, mode}
|
|
57
66
|
// ---------------------------------------------------------------------------
|
|
58
|
-
/** Compose a node's full pi launch recipe from its persona. The
|
|
59
|
-
*
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
/** Compose a node's full pi launch recipe from its persona. The system prompt
|
|
68
|
+
* is composed from FOUR inputs: kind×mode (the persona body) plus lifecycle
|
|
69
|
+
* (terminal/resident — the finish contract) and spine position (hasManager —
|
|
70
|
+
* whether the push-up family is taught at all). Callers pass the authoritative
|
|
71
|
+
* lifecycle + hasManager (`parent !== null`) so a polymorph/flip rebuilds the
|
|
72
|
+
* prompt faithfully. The two canvas extensions are always first; persona-
|
|
73
|
+
* declared extensions follow. */
|
|
74
|
+
export function buildLaunchSpec(kind, mode, opts) {
|
|
75
|
+
const p = resolvePersona(kind, mode, { lifecycle: opts.lifecycle, hasManager: opts.hasManager });
|
|
62
76
|
const launch = {
|
|
63
77
|
model: p.model !== undefined ? normalizeModel(p.model) : undefined,
|
|
64
78
|
tools: p.tools,
|
|
@@ -68,16 +82,34 @@ export function buildLaunchSpec(kind, mode, opts = {}) {
|
|
|
68
82
|
};
|
|
69
83
|
return { launch, lifecycle: p.lifecycle, skills: p.skills };
|
|
70
84
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Build the pi argv to launch / revive a node
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
/** Persist a node's (possibly large) system prompt to a file in its node dir and
|
|
89
|
+
* return the absolute path, so callers can pass a short path to pi instead of
|
|
90
|
+
* the inline text. Returns null if the write fails — the caller then falls back
|
|
91
|
+
* to passing the prompt inline. Rewritten every launch so a polymorph's updated
|
|
92
|
+
* prompt always lands. */
|
|
93
|
+
function writeSystemPromptFile(nodeId, prompt) {
|
|
94
|
+
try {
|
|
95
|
+
const dir = nodeDir(nodeId);
|
|
96
|
+
mkdirSync(dir, { recursive: true });
|
|
97
|
+
const p = join(dir, 'system-prompt.md');
|
|
98
|
+
writeFileSync(p, prompt, 'utf8');
|
|
99
|
+
return p;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
77
104
|
}
|
|
78
105
|
/** Construct the pi invocation for a node.
|
|
79
106
|
* - fresh start: pass `prompt` (the node's first user message), no resume.
|
|
80
|
-
* -
|
|
107
|
+
* - fork start: pass `forkFrom` (absolute .jsonl path or partial uuid) to `--fork`
|
|
108
|
+
* — pi COPIES that conversation into a NEW session for this node, then `prompt`
|
|
109
|
+
* is delivered as the next message. One-shot at birth: the node thereafter
|
|
110
|
+
* captures its OWN pi_session_file and revives by `--session` like any other.
|
|
111
|
+
* - revive idle/done: pass `resumeSessionPath` (absolute .jsonl path, preferred)
|
|
112
|
+
* or `resumeSessionId` (bare uuid fallback) to `--session` (keeps conversation).
|
|
81
113
|
* - refresh-yield: fresh again (no resume) — the node re-reads its roadmap. */
|
|
82
114
|
export function buildPiArgv(meta, opts = {}) {
|
|
83
115
|
const spec = meta.launch;
|
|
@@ -86,14 +118,41 @@ export function buildPiArgv(meta, opts = {}) {
|
|
|
86
118
|
argv.push('-e', ext);
|
|
87
119
|
}
|
|
88
120
|
argv.push('-n', editorLabel(meta));
|
|
89
|
-
|
|
90
|
-
|
|
121
|
+
// pi's `--resume` is a bare toggle that opens the interactive picker; the
|
|
122
|
+
// flag that resumes a *specific* session is `--session <path|id>`. Prefer the
|
|
123
|
+
// absolute FILE path when present: pi resolves a bare id cwd-relative first
|
|
124
|
+
// and shows a cross-project "Fork? [y/N]" prompt when the revive cwd differs
|
|
125
|
+
// from the session's creation cwd, whereas a path (contains `/` or ends
|
|
126
|
+
// `.jsonl`) is opened directly — immune to any cwd discrepancy. The bare uuid
|
|
127
|
+
// is the fallback for older nodes booted before pi_session_file was captured.
|
|
128
|
+
// `--fork <path|id>` is the spawn-time branch: pi copies the source session
|
|
129
|
+
// into a fresh one for this node (the source is untouched), then delivers the
|
|
130
|
+
// kickoff prompt as the next message. Mutually exclusive with `--session`
|
|
131
|
+
// (resume) — fork wins when both are somehow set, but in practice a spawn
|
|
132
|
+
// never resumes and a revive never forks.
|
|
133
|
+
if (opts.forkFrom !== undefined && opts.forkFrom !== '') {
|
|
134
|
+
argv.push('--fork', opts.forkFrom);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const resumeArg = opts.resumeSessionPath ?? opts.resumeSessionId;
|
|
138
|
+
if (resumeArg !== undefined)
|
|
139
|
+
argv.push('--session', resumeArg);
|
|
140
|
+
}
|
|
91
141
|
if (spec?.model !== undefined)
|
|
92
142
|
argv.push('--model', spec.model);
|
|
93
143
|
if (spec?.tools !== undefined && spec.tools.length > 0)
|
|
94
144
|
argv.push('--tools', spec.tools.join(','));
|
|
95
145
|
if (spec?.systemPrompt !== undefined && spec.systemPrompt !== '') {
|
|
96
|
-
|
|
146
|
+
// pi's --append-system-prompt reads a FILE when the arg is an existing path,
|
|
147
|
+
// else treats the arg as literal text. Pass the prompt as a file path, not
|
|
148
|
+
// inline: an orchestrator persona is ~17KB, and passed inline it inflates the
|
|
149
|
+
// `tmux new-window 'pi …'` command past tmux's command-length limit, so the
|
|
150
|
+
// spawn dies with "command too long" and the node is marked dead before pi
|
|
151
|
+
// ever starts (base workers fit, orchestrator children don't). Writing it to
|
|
152
|
+
// the node dir keeps the command tiny. Falls back to inline if the write
|
|
153
|
+
// fails (e.g. an ephemeral meta with no node dir).
|
|
154
|
+
const promptArg = writeSystemPromptFile(meta.node_id, spec.systemPrompt) ?? spec.systemPrompt;
|
|
155
|
+
argv.push('--append-system-prompt', promptArg);
|
|
97
156
|
}
|
|
98
157
|
if (opts.prompt !== undefined && opts.prompt !== '')
|
|
99
158
|
argv.push(opts.prompt);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NodeMeta } from '../canvas/types.js';
|
|
2
|
+
/** The lifecycle events — the only vocabulary for moving a node's status/intent.
|
|
3
|
+
* Each maps (in the table below) to a target status and/or intent plus the set
|
|
4
|
+
* of from-statuses it is legal from. */
|
|
5
|
+
export type LifecycleEvent = 'finalize' | 'reap' | 'cancel' | 'crash' | 'yield' | 'release' | 'revive' | 'boot';
|
|
6
|
+
/** Enact a lifecycle event on a node: validate the from-status against the
|
|
7
|
+
* table, then write status+intent in ONE atomic statement (so they can never
|
|
8
|
+
* disagree). Returns the hydrated node view after the write.
|
|
9
|
+
*
|
|
10
|
+
* Throws on an unknown node, or on an ILLEGAL move (e.g. `finalize` on a `dead`
|
|
11
|
+
* node) — illegal states are unrepresentable. The throw is a real signal:
|
|
12
|
+
* callers that previously swallowed db-mutation errors now surface them. */
|
|
13
|
+
export declare function transition(nodeId: string, event: LifecycleEvent): NodeMeta;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// lifecycle.ts — the node status×intent state machine.
|
|
2
|
+
//
|
|
3
|
+
// ONE place defines which (status, intent) moves are legal and enacts them.
|
|
4
|
+
// Before this, ~a dozen scattered setStatus()/setIntent() pairs across
|
|
5
|
+
// reset/close/revive/feed/daemon/stophook/queue/promote re-derived the lifecycle
|
|
6
|
+
// by hand, with no shared definition of "what move is legal." Here the legal
|
|
7
|
+
// transition TABLE is the definition, and `transition(id, event)` is the single
|
|
8
|
+
// writer of status+intent: it validates the from-status, then writes both fields
|
|
9
|
+
// in ONE atomic statement (built on Phase 2's WAL'd row setters) so the two can
|
|
10
|
+
// never disagree.
|
|
11
|
+
//
|
|
12
|
+
// This mirrors persona.ts: persona.ts is the single source of transition PROSE;
|
|
13
|
+
// lifecycle.ts is the single source of which status/intent move is LEGAL. Two
|
|
14
|
+
// parallel, legible state machines instead of scattered enactment.
|
|
15
|
+
//
|
|
16
|
+
// Crash-safety invariant (was a comment repeated in reset/close/reapDescendants):
|
|
17
|
+
// "flip status to a non-supervised value + clear intent BEFORE killing the
|
|
18
|
+
// window" — the daemon only ever revives active|idle nodes, so a teardown must
|
|
19
|
+
// leave the node done/canceled first to close the revive race. That invariant is
|
|
20
|
+
// now the DEFINITION of the `reap`/`cancel` events: callers flip via transition()
|
|
21
|
+
// and only THEN kill the window.
|
|
22
|
+
//
|
|
23
|
+
// Layering note: lifecycle.ts is runtime, but it is the canvas write surface's
|
|
24
|
+
// `transition` verb (the only writer of status+intent), so it owns its atomic
|
|
25
|
+
// row UPDATE directly via openDb — the one sanctioned exception to "only
|
|
26
|
+
// canvas.ts touches the db" (see canvas/CLAUDE.md), exactly as db.ts's backfill
|
|
27
|
+
// is the sanctioned exception for a data migration.
|
|
28
|
+
import { openDb, getNode } from '../canvas/index.js';
|
|
29
|
+
const ANY = '*';
|
|
30
|
+
/** The supervised statuses — a live node the daemon watches. */
|
|
31
|
+
const LIVE = ['active', 'idle'];
|
|
32
|
+
/** The legal transition table — derived directly from the (status, intent) pairs
|
|
33
|
+
* the runtime actually wrote at its audited call sites, so behavior is preserved
|
|
34
|
+
* by construction. Each entry's comment names its writer(s). */
|
|
35
|
+
const TRANSITIONS = {
|
|
36
|
+
// feed.push(final) · queue.cancelJob · markCleanExitDone (clean quit).
|
|
37
|
+
finalize: { status: 'done', intent: 'done', from: LIVE },
|
|
38
|
+
// reapDescendants · relaunchRoot park-old. Forced teardown → done, intent cleared.
|
|
39
|
+
reap: { status: 'done', intent: null, from: ANY },
|
|
40
|
+
// closeNode cascade. Forced teardown → canceled, intent cleared.
|
|
41
|
+
cancel: { status: 'canceled', intent: null, from: ANY },
|
|
42
|
+
// daemon superviseTick: window gone with no yield/release intent. Intent KEPT
|
|
43
|
+
// (the dead log line still reports it).
|
|
44
|
+
crash: { status: 'dead', from: LIVE },
|
|
45
|
+
// requestYield · relaunchRoot new-node safety net. Status KEPT (already active).
|
|
46
|
+
yield: { intent: 'refresh', from: LIVE },
|
|
47
|
+
// stophook idle-release: free the window, stay woken by the inbox.
|
|
48
|
+
release: { status: 'idle', intent: 'idle-release', from: LIVE },
|
|
49
|
+
// reviveNode · resetRoot · stophook boot-confirm (clear a pending refresh net).
|
|
50
|
+
revive: { status: 'active', intent: null, from: ANY },
|
|
51
|
+
// reviveInPlace: re-exec a fresh pi in the SAME pane. Status (re)affirmed
|
|
52
|
+
// active; intent KEPT so a pending refresh survives as proof-of-boot until the
|
|
53
|
+
// fresh pi's session_start clears it (a premature clear is how a failed
|
|
54
|
+
// respawn became a silent death — see revive.ts).
|
|
55
|
+
boot: { status: 'active', from: LIVE },
|
|
56
|
+
};
|
|
57
|
+
/** Enact a lifecycle event on a node: validate the from-status against the
|
|
58
|
+
* table, then write status+intent in ONE atomic statement (so they can never
|
|
59
|
+
* disagree). Returns the hydrated node view after the write.
|
|
60
|
+
*
|
|
61
|
+
* Throws on an unknown node, or on an ILLEGAL move (e.g. `finalize` on a `dead`
|
|
62
|
+
* node) — illegal states are unrepresentable. The throw is a real signal:
|
|
63
|
+
* callers that previously swallowed db-mutation errors now surface them. */
|
|
64
|
+
export function transition(nodeId, event) {
|
|
65
|
+
const spec = TRANSITIONS[event];
|
|
66
|
+
const cur = getNode(nodeId);
|
|
67
|
+
if (cur === null)
|
|
68
|
+
throw new Error(`transition: unknown node ${nodeId}`);
|
|
69
|
+
if (spec.from !== ANY && !spec.from.includes(cur.status)) {
|
|
70
|
+
throw new Error(`illegal lifecycle transition: '${event}' from status='${cur.status}' (node ${nodeId})`);
|
|
71
|
+
}
|
|
72
|
+
const writeStatus = Object.prototype.hasOwnProperty.call(spec, 'status');
|
|
73
|
+
const writeIntent = Object.prototype.hasOwnProperty.call(spec, 'intent');
|
|
74
|
+
const db = openDb();
|
|
75
|
+
if (writeStatus && writeIntent) {
|
|
76
|
+
db.prepare('UPDATE nodes SET status = ?, intent = ? WHERE node_id = ?')
|
|
77
|
+
.run(spec.status, spec.intent ?? null, nodeId);
|
|
78
|
+
}
|
|
79
|
+
else if (writeStatus) {
|
|
80
|
+
db.prepare('UPDATE nodes SET status = ? WHERE node_id = ?').run(spec.status, nodeId);
|
|
81
|
+
}
|
|
82
|
+
else if (writeIntent) {
|
|
83
|
+
db.prepare('UPDATE nodes SET intent = ? WHERE node_id = ?').run(spec.intent ?? null, nodeId);
|
|
84
|
+
}
|
|
85
|
+
return getNode(nodeId);
|
|
86
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** The node-local index template. Named export kept for callers/tests that
|
|
2
|
+
* assert the seeded node store verbatim. */
|
|
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. */
|
|
10
|
+
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;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// MEMORY.md + memory/ — an orchestrator's persistent file-based memory.
|
|
2
|
+
//
|
|
3
|
+
// One layout, three scopes. Each store is a `memory/` directory of one-fact
|
|
4
|
+
// files (each with typed frontmatter and [[wikilinks]]) indexed by a single
|
|
5
|
+
// MEMORY.md that holds one pointer line per memory and NEVER any content — the
|
|
6
|
+
// architecture in examples/memory-instructions.md. The pointer lines are the
|
|
7
|
+
// load-bearing read: a node's <crtr-context> bearings block extracts every
|
|
8
|
+
// applicable store's pointer lines each brand-new chat (see canvas-context-intro
|
|
9
|
+
// + bearings), so the indexes must stay lean; the detail files load on demand
|
|
10
|
+
// mid-session.
|
|
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. */
|
|
80
|
+
export function memoryDir(nodeId) {
|
|
81
|
+
return join(contextDir(nodeId), 'memory');
|
|
82
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NodeMeta } from '../canvas/index.js';
|
|
2
|
+
/** Coerce arbitrary text into a 3-5 word kebab-case name, or '' if nothing
|
|
3
|
+
* usable survives. Lowercases, keeps [a-z0-9], collapses everything else to a
|
|
4
|
+
* single hyphen, and clamps to the first 5 words. */
|
|
5
|
+
export declare function sanitizeSessionName(raw: string): string;
|
|
6
|
+
/** Local fallback: derive a name straight from the prompt (no pi call). Drops
|
|
7
|
+
* stop-words, takes the first few content words. */
|
|
8
|
+
export declare function slugFromPrompt(prompt: string): string;
|
|
9
|
+
/** Synchronously ask pi for a 2-4 word kebab name for `prompt`. Blocks up to
|
|
10
|
+
* NAME_TIMEOUT_MS; on any failure (non-zero exit, timeout, empty/garbled
|
|
11
|
+
* output) falls back to a local slug. Returns '' only for an empty prompt. */
|
|
12
|
+
export declare function generateSessionName(prompt: string): string;
|
|
13
|
+
/** Asynchronously generate a name for `prompt` and persist it to the node's
|
|
14
|
+
* meta as `description` — only if the node has none yet (so a later message
|
|
15
|
+
* never clobbers it). Non-blocking: safe to call from inside a live pi event
|
|
16
|
+
* loop. Best-effort; swallows all errors.
|
|
17
|
+
*
|
|
18
|
+
* `onNamed` (optional) fires with the freshly-persisted meta the moment the
|
|
19
|
+
* name lands — the bare-root path passes a callback that calls
|
|
20
|
+
* pi.setSessionName(editorLabel(meta)) so the LIVE editor label updates in the
|
|
21
|
+
* same session, instead of waiting for the next revive/cycle. */
|
|
22
|
+
export declare function generateAndPersistName(nodeId: string, prompt: string, onNamed?: (meta: NodeMeta) => void): void;
|