@north-light/crouter 0.3.175 → 0.3.177
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/api/client.d.ts +4 -4
- package/dist/api/client.js +2 -2
- package/dist/api/dto/lifecycle.d.ts +1 -1
- package/dist/api/dto/nodes.d.ts +7 -1
- package/dist/api/dto/profiles.d.ts +3 -0
- package/dist/api/dto/reports.d.ts +1 -1
- package/dist/clients/attach/__tests__/group-activity.test.js +2 -2
- package/dist/clients/attach/config.js +5 -5
- package/dist/clients/attach/input/clipboard-image.js +2 -2
- package/dist/clients/attach/input/ref-autocomplete.js +1 -1
- package/dist/clients/attach/input/titled-editor.d.ts +12 -13
- package/dist/clients/attach/input/titled-editor.js +14 -15
- package/dist/clients/attach/overlays/dialogs.d.ts +1 -1
- package/dist/clients/attach/overlays/dialogs.js +2 -2
- package/dist/clients/attach/overlays/mcp.js +5 -12
- package/dist/clients/attach/render/chat-view.d.ts +1 -1
- package/dist/clients/attach/render/chat-view.js +2 -2
- package/dist/clients/attach/render/crtr-output.d.ts +3 -3
- package/dist/clients/attach/render/crtr-output.js +25 -13
- package/dist/clients/attach/render/group-recap.js +7 -0
- package/dist/clients/attach/session/context.js +3 -3
- package/dist/clients/attach/session/editor-inventory.js +6 -6
- package/dist/clients/attach/session/input-wiring.js +1 -1
- package/dist/clients/attach/session/teardown.js +7 -9
- package/dist/clients/attach/slash/dispatch.d.ts +1 -2
- package/dist/clients/attach/slash/dispatch.js +4 -8
- package/dist/clients/attach/viewer.js +536 -566
- package/dist/clients/inbox/controller.js +3 -2
- package/dist/clients/inbox/deck-adapter.js +3 -2
- package/dist/clients/surfaces/host.d.ts +1 -1
- package/dist/commands/api-client.d.ts +12 -13
- package/dist/commands/api-client.js +15 -16
- package/dist/commands/chord.js +1 -2
- package/dist/commands/human/queue.js +6 -5
- package/dist/commands/memory/find.js +3 -3
- package/dist/commands/node/create.js +7 -3
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node-lifecycle-revive.d.ts +1 -1
- package/dist/commands/node-lifecycle-revive.js +3 -3
- package/dist/commands/profile/list.js +3 -1
- package/dist/commands/profile/new.js +4 -1
- package/dist/commands/profile/project.js +23 -9
- package/dist/commands/profile/show.js +3 -1
- package/dist/commands/profile.js +2 -2
- package/dist/commands/push.js +1 -1
- package/dist/commands/surface/node/placement.d.ts +1 -1
- package/dist/commands/surface/node/placement.js +1 -1
- package/dist/commands/surface-edit.js +1 -4
- package/dist/commands/sys/logs.js +4 -2
- package/dist/commands/sys/panels/panel.d.ts +3 -3
- package/dist/commands/sys/panels/panel.js +3 -3
- package/dist/commands/sys/setup-core.js +1 -7
- package/dist/commands/sys/sync-deps.js +1 -3
- package/dist/commands/sys/sync-project-guidance.js +2 -11
- package/dist/commands/sys/sysprompt.js +1 -3
- package/dist/core/__tests__/branded-host-concurrency.test.js +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +1 -1
- package/dist/core/__tests__/helpers/harness.d.ts +1 -1
- package/dist/core/__tests__/helpers/harness.js +1 -1
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +1 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +1 -1
- package/dist/core/canvas/boot.js +1 -1
- package/dist/core/canvas/browse/app.js +2 -2
- package/dist/core/canvas/canvas.d.ts +3 -3
- package/dist/core/canvas/canvas.js +3 -3
- package/dist/core/canvas/daemon-owner.js +1 -2
- package/dist/core/canvas/db.js +7 -8
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +7 -1
- package/dist/core/canvas/index.js +2 -2
- package/dist/core/canvas/pid.d.ts +12 -14
- package/dist/core/canvas/pid.js +16 -20
- package/dist/core/canvas/remote-transport.js +2 -2
- package/dist/core/canvas/status-glyph.d.ts +1 -1
- package/dist/core/canvas/status-glyph.js +1 -1
- package/dist/core/canvas/types.d.ts +1 -1
- package/dist/core/command-manifests/manifest.js +4 -6
- package/dist/core/command-manifests/schema.js +10 -12
- package/dist/core/command-plugins/transport/exec-invoke.js +5 -7
- package/dist/core/command-plugins/transport/http-invoke.js +14 -8
- package/dist/core/command.js +3 -1
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +6 -29
- package/dist/core/events/read.js +1 -3
- package/dist/core/exclusive-lock.d.ts +14 -0
- package/dist/core/exclusive-lock.js +124 -0
- package/dist/core/fault-classifier.js +1 -3
- package/dist/core/fs-utils.d.ts +7 -0
- package/dist/core/fs-utils.js +29 -2
- package/dist/core/host-exports/export.d.ts +4 -5
- package/dist/core/host-exports/export.js +4 -5
- package/dist/core/human/claim.js +4 -2
- package/dist/core/human/convention.d.ts +0 -11
- package/dist/core/human/convention.js +1 -131
- package/dist/core/human/scan.js +5 -4
- package/dist/core/io.d.ts +23 -0
- package/dist/core/io.js +60 -0
- package/dist/core/preview-registry.d.ts +10 -5
- package/dist/core/preview-registry.js +136 -2537
- package/dist/core/profiles/manifest.d.ts +13 -2
- package/dist/core/profiles/manifest.js +58 -49
- package/dist/core/review/store.d.ts +4 -1
- package/dist/core/review/store.js +10 -0
- package/dist/core/review/types.d.ts +1 -1
- package/dist/core/review/types.js +1 -1
- package/dist/core/runtime/branded-host.js +6 -84
- package/dist/core/runtime/broker/frame-dispatch.js +4 -4
- package/dist/core/runtime/broker-cli.js +1 -1
- package/dist/core/runtime/broker-extension-render.js +1 -1
- package/dist/core/runtime/broker-protocol.d.ts +9 -10
- package/dist/core/runtime/broker-protocol.js +9 -10
- package/dist/core/runtime/broker-sdk.d.ts +1 -1
- package/dist/core/runtime/broker.js +6 -6
- package/dist/core/runtime/canvas-extensions.d.ts +1 -0
- package/dist/core/runtime/canvas-extensions.js +2 -0
- package/dist/core/runtime/host.js +14 -14
- package/dist/core/runtime/launch.d.ts +1 -1
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +16 -21
- package/dist/core/runtime/node-read.js +2 -3
- package/dist/core/runtime/package-health.js +1 -8
- package/dist/core/runtime/persona.js +2 -2
- package/dist/core/runtime/pi-vendored.d.ts +4 -2
- package/dist/core/runtime/pi-vendored.js +5 -16
- package/dist/core/runtime/promote.js +1 -1
- package/dist/core/runtime/reopen.js +2 -2
- package/dist/core/runtime/revive-all.d.ts +3 -3
- package/dist/core/runtime/revive-all.js +6 -6
- package/dist/core/runtime/revive.js +1 -1
- package/dist/core/runtime/spawn.d.ts +1 -2
- package/dist/core/runtime/spawn.js +2 -3
- package/dist/core/runtime/structured-output.js +4 -6
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/runtime/tmux-driver.d.ts +2 -2
- package/dist/core/runtime/tmux-driver.js +3 -5
- package/dist/core/runtime/tool-group-summary.js +5 -4
- package/dist/core/runtime/warm-pool.d.ts +9 -8
- package/dist/core/runtime/warm-pool.js +58 -62
- package/dist/core/scope.js +2 -9
- package/dist/core/self-update.js +3 -3
- package/dist/core/spawn.d.ts +0 -1
- package/dist/core/spawn.js +1 -4
- package/dist/core/substrate/index.d.ts +2 -1
- package/dist/core/substrate/index.js +2 -1
- package/dist/core/substrate/on-read-node.d.ts +7 -0
- package/dist/core/substrate/on-read-node.js +37 -0
- package/dist/core/substrate/on-read.d.ts +7 -5
- package/dist/core/substrate/on-read.js +16 -32
- package/dist/core/termrender/termrender.js +16 -96
- package/dist/core/wake.js +2 -2
- package/dist/core/worktree.d.ts +3 -3
- package/dist/core/worktree.js +16 -107
- package/dist/daemon/api/bridge.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +1 -2
- package/dist/daemon/api/handlers/messages.js +2 -2
- package/dist/daemon/api/handlers/nodes.js +33 -10
- package/dist/daemon/api/handlers/reports.js +1 -1
- package/dist/daemon/api/handlers/review-comments.js +5 -44
- package/dist/daemon/api/handlers/reviews.js +5 -53
- package/dist/daemon/api/handlers/validate.d.ts +23 -0
- package/dist/daemon/api/handlers/validate.js +58 -0
- package/dist/daemon/api/map.js +1 -0
- package/dist/daemon/cron-run.js +7 -7
- package/dist/daemon/human/finish.js +3 -2
- package/dist/daemon/manage.d.ts +6 -6
- package/dist/daemon/manage.js +15 -16
- package/dist/daemon/review/finish.js +1 -10
- package/dist/pi-extensions/canvas-context-intro.js +1 -14
- package/dist/pi-extensions/canvas-doc-substrate.js +2 -2
- package/dist/pi-extensions/canvas-preview-result.d.ts +22 -0
- package/dist/pi-extensions/canvas-preview-result.js +60 -0
- package/dist/pi-extensions/canvas-review-boundary.js +1 -14
- package/dist/pi-extensions/truncate.d.ts +5 -0
- package/dist/pi-extensions/truncate.js +14 -0
- package/dist/shared/predicates.d.ts +2 -0
- package/dist/shared/predicates.js +4 -0
- package/dist/shared/shell-quote.d.ts +3 -0
- package/dist/shared/shell-quote.js +5 -0
- package/dist/shared/tool-groups.d.ts +4 -2
- package/dist/shared/tool-groups.js +3 -3
- package/dist/types.d.ts +8 -5
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/core/substrate/on-read-pure.d.ts +0 -7
- package/dist/core/substrate/on-read-pure.js +0 -272
|
@@ -127,7 +127,7 @@ const BROKER_SIGKILL_GRACE_MS = 2_000;
|
|
|
127
127
|
/** How often `pollUntilTreeDead` re-checks the process TREE. Short enough that
|
|
128
128
|
* the common/happy path (tree already dead) pays only a couple of ticks; the
|
|
129
129
|
* full grace ceilings above are reserved for the genuinely-wedged case,
|
|
130
|
-
* where the added latency is the whole point
|
|
130
|
+
* where the added latency is the whole point. */
|
|
131
131
|
const TEARDOWN_POLL_MS = 150;
|
|
132
132
|
/** Poll `isAlive` every `TEARDOWN_POLL_MS` until it reports dead. If it is
|
|
133
133
|
* STILL alive once `timeoutMs` elapses, fire `onTimeout` exactly once (the
|
|
@@ -136,7 +136,7 @@ const TEARDOWN_POLL_MS = 150;
|
|
|
136
136
|
* up (an unkillable/D-state descendant is out of scope for this primitive).
|
|
137
137
|
*
|
|
138
138
|
* Deliberately a recursive, NEVER-`.unref()`'d `setTimeout` rather than a
|
|
139
|
-
* fixed unref'd wait
|
|
139
|
+
* fixed unref'd wait: most teardown callers
|
|
140
140
|
* are short-lived CLI subprocesses (`node lifecycle close`, `canvas prune`)
|
|
141
141
|
* that would otherwise exit before an unref'd SIGKILL timer ever fires,
|
|
142
142
|
* silently skipping the last-resort rung. Polling (instead of a blind fixed
|
|
@@ -167,7 +167,7 @@ function pollUntil(isAlive, timeoutMs, onTimeout, hardCeilingMs = timeoutMs + 5_
|
|
|
167
167
|
* `captureTeardownSnapshot` identity map over that same list) — both taken
|
|
168
168
|
* ONCE by `teardown()` synchronously at its very top, before EITHER
|
|
169
169
|
* escalation path (connect-error or post-shutdown-frame) can run, and
|
|
170
|
-
* threaded through here unchanged
|
|
170
|
+
* threaded through here unchanged. This
|
|
171
171
|
* function must NEVER re-walk the process tree itself: `launch()` spawns the
|
|
172
172
|
* broker `detached: true`, so
|
|
173
173
|
* signaling the broker's OWN group (the first thing `killProcessTreePids`
|
|
@@ -180,8 +180,8 @@ function pollUntil(isAlive, timeoutMs, onTimeout, hardCeilingMs = timeoutMs + 5_
|
|
|
180
180
|
* "lose" a still-alive detached descendant — e.g. the pi bash tool's shell
|
|
181
181
|
* child, itself `detached: true` and thus its own group leader, distinct
|
|
182
182
|
* from the broker's group, so a plain `kill(-brokerPid)` never reaches it
|
|
183
|
-
* either
|
|
184
|
-
* PID REUSE across the multi-second escalation window
|
|
183
|
+
* either. `identities` guards each signal against
|
|
184
|
+
* PID REUSE across the multi-second escalation window —
|
|
185
185
|
* see `killProcessTreePids`. `null` means the initial `captureTeardownSnapshot`
|
|
186
186
|
* probe itself failed (no baseline to compare against) — `killProcessTreePids`
|
|
187
187
|
* falls through to its normal unguarded signal path in that case, never
|
|
@@ -312,19 +312,19 @@ export const headlessBrokerHost = {
|
|
|
312
312
|
// won't revive. On connect failure (broker dead/crashed/wedged) fall back
|
|
313
313
|
// to escalateBrokerTeardown — a tree-wide SIGTERM now, a tree-wide SIGKILL
|
|
314
314
|
// as the last resort — so no orphaned descendant of a dead/wedged broker
|
|
315
|
-
// survives the node
|
|
315
|
+
// survives the node — + unlink the stale socket.
|
|
316
316
|
//
|
|
317
317
|
// Capture the broker pid ONCE, synchronously, right here — never re-query
|
|
318
318
|
// `getNode(nodeId)` inside the async callbacks/timers below. Callers such
|
|
319
319
|
// as placement.ts's `reapIfEmpty()` call `teardown()` then immediately
|
|
320
320
|
// `deleteNode()` in the same tick; by the time an async callback fires the
|
|
321
321
|
// row can already be gone, so a re-query would silently resolve to
|
|
322
|
-
// `undefined` and skip escalation entirely
|
|
322
|
+
// `undefined` and skip escalation entirely.
|
|
323
323
|
const node = getNode(nodeId);
|
|
324
324
|
const pid = node?.pi_pid;
|
|
325
|
-
// Snapshot the full descendant TREE
|
|
325
|
+
// Snapshot the full descendant TREE — the
|
|
326
326
|
// root pid plus every transitive descendant — and a stable identity
|
|
327
|
-
// fingerprint for every pid in it
|
|
327
|
+
// fingerprint for every pid in it, BOTH from ONE `ps`
|
|
328
328
|
// table read (`captureTeardownSnapshot`, final review: closes the small
|
|
329
329
|
// race between two separate probes), exactly ONCE, right here, while the
|
|
330
330
|
// broker (if alive) still holds parentage over any detached descendant.
|
|
@@ -393,10 +393,10 @@ export const headlessBrokerHost = {
|
|
|
393
393
|
// Bounded exit confirmation: a broker that connects fine but then HANGS
|
|
394
394
|
// inside session.dispose() (disposeAndExit catches a throw, not a hang)
|
|
395
395
|
// would leak the process holding the sole .jsonl writer — and any
|
|
396
|
-
// bash→test→app descendants it spawned along the way
|
|
396
|
+
// bash→test→app descendants it spawned along the way. Poll
|
|
397
397
|
// the whole process TREE (not just the broker's own pid) for up to
|
|
398
|
-
// `BROKER_SHUTDOWN_GRACE_MS`, UNCONDITIONAL on the broker's own liveness
|
|
399
|
-
//
|
|
398
|
+
// `BROKER_SHUTDOWN_GRACE_MS`, UNCONDITIONAL on the broker's own liveness:
|
|
399
|
+
// a broker that exits cleanly can still
|
|
400
400
|
// leave a live descendant behind (dispose() partially failed, or never
|
|
401
401
|
// reached a detached SDK child), which is exactly the shape this
|
|
402
402
|
// closes. `pollUntil` is ref'd — not `.unref()`'d — so a short-lived
|
|
@@ -405,8 +405,8 @@ export const headlessBrokerHost = {
|
|
|
405
405
|
// couple of poll ticks.
|
|
406
406
|
//
|
|
407
407
|
// Checks `isAnyPidAlive(tree)` against the FIXED pre-signal snapshot,
|
|
408
|
-
// NEVER a fresh `isProcessTreeAlive`/`descendantPids` re-walk
|
|
409
|
-
//
|
|
408
|
+
// NEVER a fresh `isProcessTreeAlive`/`descendantPids` re-walk: the broker
|
|
409
|
+
// can exit at any point during this
|
|
410
410
|
// grace window while leaving a detached descendant alive, and the
|
|
411
411
|
// instant it does, the kernel reparents that descendant away from `pid`
|
|
412
412
|
// — a re-walk-by-ppid would then find nothing and silently skip
|
|
@@ -56,7 +56,7 @@ export declare function nextLadderModel(currentSpec: string, direction?: 1 | -1,
|
|
|
56
56
|
* 3. Unset → pi default.
|
|
57
57
|
* Callers pass the authoritative lifecycle + hasManager (`parent !== null`) so
|
|
58
58
|
* a polymorph/flip rebuilds the recipe faithfully; `lifecycle` is returned
|
|
59
|
-
* as-given
|
|
59
|
+
* as-given, never derived from persona frontmatter. The two canvas
|
|
60
60
|
* extensions are always first; kind-declared extensions follow. */
|
|
61
61
|
export declare function buildLaunchSpec(kind: string, mode: Mode, opts: {
|
|
62
62
|
lifecycle: Lifecycle;
|
|
@@ -249,7 +249,7 @@ export function nextLadderModel(currentSpec, direction = 1, providers) {
|
|
|
249
249
|
* 3. Unset → pi default.
|
|
250
250
|
* Callers pass the authoritative lifecycle + hasManager (`parent !== null`) so
|
|
251
251
|
* a polymorph/flip rebuilds the recipe faithfully; `lifecycle` is returned
|
|
252
|
-
* as-given
|
|
252
|
+
* as-given, never derived from persona frontmatter. The two canvas
|
|
253
253
|
* extensions are always first; kind-declared extensions follow. */
|
|
254
254
|
export function buildLaunchSpec(kind, mode, opts) {
|
|
255
255
|
const merged = readMergedLaunchConfig(opts.cwd ?? process.cwd(), opts.profileId !== undefined ? opts.profileId : (process.env['CRTR_PROFILE_ID'] || null));
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
// lifecycle.ts — the node status×intent state machine.
|
|
2
2
|
//
|
|
3
3
|
// ONE place defines which (status, intent) moves are legal and enacts them.
|
|
4
|
-
//
|
|
5
|
-
// reset
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// in ONE atomic statement (built on Phase 2's WAL'd row setters) so the two can
|
|
10
|
-
// never disagree.
|
|
4
|
+
// The legal transition TABLE is the definition, and `transition(id, event)` is
|
|
5
|
+
// the single writer of status+intent for every caller (reset, close, revive,
|
|
6
|
+
// feed, daemon, stophook, queue, promote): it validates the from-status, then
|
|
7
|
+
// writes both fields in ONE atomic statement (on the WAL'd row setters) so the
|
|
8
|
+
// two can never disagree.
|
|
11
9
|
//
|
|
12
10
|
// This mirrors persona.ts: persona.ts is the single source of transition PROSE;
|
|
13
11
|
// lifecycle.ts is the single source of which status/intent move is LEGAL. Two
|
|
14
12
|
// parallel, legible state machines instead of scattered enactment.
|
|
15
13
|
//
|
|
16
|
-
// Crash-safety invariant
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// now the DEFINITION of the `cancel` event: callers flip via transition()
|
|
22
|
-
// and only THEN tear the host down.
|
|
14
|
+
// Crash-safety invariant: flip status to a non-supervised value + clear intent
|
|
15
|
+
// BEFORE killing the host (tmux pane or headless broker) — the daemon only ever
|
|
16
|
+
// revives active|idle nodes, so a teardown must leave the node done/canceled
|
|
17
|
+
// first to close the revive race. That invariant is the DEFINITION of the
|
|
18
|
+
// `cancel` event: callers flip via transition() and only THEN tear the host down.
|
|
23
19
|
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// is
|
|
29
|
-
//
|
|
30
|
-
// it was COLLAPSED into `cancel`; reset.ts's reapDescendants routes through
|
|
20
|
+
// An externally-reaped node — torn down because the user moved on (close
|
|
21
|
+
// cascade) OR because a root relaunch superseded its workers — ends `canceled`,
|
|
22
|
+
// NOT `done`. `done` is reserved for a node that finished its OWN work (finish)
|
|
23
|
+
// — including a relaunched root, which is parked `done` as history, not
|
|
24
|
+
// canceled. There is no separate reap event: it would be identical to `cancel`
|
|
25
|
+
// in every field and side effect, so reset.ts's reapDescendants routes through
|
|
31
26
|
// `cancel`.
|
|
32
27
|
//
|
|
33
28
|
// Layering note: lifecycle.ts is runtime, but it is the canvas write surface's
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
// These are canvas/runtime reads (getNode + SessionManager + session-cycles):
|
|
5
5
|
// they belong SERVER-SIDE. crtrd's node read handlers (daemon/api/handlers/
|
|
6
6
|
// nodes.ts) call them behind `GET /v1/nodes/{id}/{snapshot,transcript}`. They
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// so the CLI leaves' import graph reaches no canvas state store (spec §1/§10).
|
|
7
|
+
// live here rather than in the CLI leaves so the leaves' import graph reaches
|
|
8
|
+
// no canvas state store (spec §1/§10).
|
|
10
9
|
import { copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
11
10
|
import { readJsonIfExists } from '../fs-utils.js';
|
|
12
11
|
import { tmpdir } from 'node:os';
|
|
@@ -14,6 +14,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
14
14
|
import { homedir } from 'node:os';
|
|
15
15
|
import { join, resolve } from 'node:path';
|
|
16
16
|
import { isRendererReady } from '../termrender/termrender.js';
|
|
17
|
+
import { expandTilde } from '../fs-utils.js';
|
|
17
18
|
/** A non-fatal, action-oriented diagnostic for an unprovisioned managed
|
|
18
19
|
* renderer — the one remaining out-of-tree dependency. Deliberately calls
|
|
19
20
|
* `isRendererReady()`, NOT `ensureRenderer()`: this runs on the front-door boot
|
|
@@ -25,14 +26,6 @@ export function rendererWarning() {
|
|
|
25
26
|
return 'crtr: the pinned termrender renderer is not provisioned — decks, reviews, and inline diagrams render as plaintext.\n' +
|
|
26
27
|
' Fix: run `crtr sys setup` (requires `uv`: curl -LsSf https://astral.sh/uv/install.sh | sh).';
|
|
27
28
|
}
|
|
28
|
-
/** Expand a leading `~` against `homeDir` (pi's own convention). */
|
|
29
|
-
function expandTilde(pathValue, homeDir) {
|
|
30
|
-
if (pathValue === '~')
|
|
31
|
-
return homeDir;
|
|
32
|
-
if (pathValue.startsWith('~/'))
|
|
33
|
-
return join(homeDir, pathValue.slice(2));
|
|
34
|
-
return pathValue;
|
|
35
|
-
}
|
|
36
29
|
/**
|
|
37
30
|
* The pi agent config dir (`~/.pi/agent`, or `$PI_CODING_AGENT_DIR`) — a local
|
|
38
31
|
* reimplementation of pi's `getAgentDir()`. Reimplemented (not imported) ON
|
|
@@ -30,7 +30,7 @@ import { orchestratorContextNote } from './bearings.js';
|
|
|
30
30
|
/** Load a builtin/user/project memory doc's body by its normalized name, or ''
|
|
31
31
|
* if it can't be resolved. The single source for the lifecycle/spine/
|
|
32
32
|
* orchestration-kernel fragments this injector re-delivers on a transition —
|
|
33
|
-
* the SAME docs
|
|
33
|
+
* the SAME docs under `src/builtin-memory/`, so static
|
|
34
34
|
* (system-prompt splice) and transition prose can never drift. */
|
|
35
35
|
function loadMemoryBody(name) {
|
|
36
36
|
try {
|
|
@@ -48,7 +48,7 @@ function loadMemoryBody(name) {
|
|
|
48
48
|
* resource is its own context window. Roadmap-shaping guidance is just another orchestrator-gated memory
|
|
49
49
|
* doc the node already gets via the boot-render splice (kinds/<kind>/orchestrator
|
|
50
50
|
* gated `{kind, mode: orchestrator}`). (Lifecycle is left to its own section —
|
|
51
|
-
* promotion
|
|
51
|
+
* promotion does not force resident, so this never asserts residency.) */
|
|
52
52
|
function orchestrationGuidance(nodeId, kind) {
|
|
53
53
|
const kernel = loadMemoryBody('orchestration-kernel');
|
|
54
54
|
const roadmap = readRoadmap(nodeId) ?? '(no roadmap yet)';
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* heavy pi SDK index on crtr's front-door hot path (the reason broker-sdk.ts
|
|
5
5
|
* dynamic-imports the engine). Mirrors pi: `PI_CODING_AGENT_DIR` env, else
|
|
6
6
|
* `~/.pi/agent` (APP_NAME defaults to 'pi'). Re-sync on a pi SDK bump that moves
|
|
7
|
-
* the sessions dir — same vendoring rationale as the slash-command list below.
|
|
7
|
+
* the sessions dir — same vendoring rationale as the slash-command list below.
|
|
8
|
+
* `PI_CODING_AGENT_DIR` goes through the same tilde expansion pi's
|
|
9
|
+
* `getAgentDir()` applies, or `~/custom-pi` yields a broken relative path. */
|
|
8
10
|
export declare function piSessionsRoot(): string;
|
|
9
11
|
/**
|
|
10
12
|
* pi's builtin slash commands — vendored verbatim from pi `core/slash-commands.js`
|
|
@@ -13,7 +15,7 @@ export declare function piSessionsRoot(): string;
|
|
|
13
15
|
* Builtins are NOT engine-interpreted (`session.prompt('/model')` ships `/model`
|
|
14
16
|
* to the LLM as literal text), so the broker's `get_commands` op MERGES this list
|
|
15
17
|
* with the engine's registered commands/templates/skills, and the viewer
|
|
16
|
-
* autocomplete parses these locally. Both the broker
|
|
18
|
+
* autocomplete parses these locally. Both the broker and the viewer
|
|
17
19
|
* import this single copy.
|
|
18
20
|
*
|
|
19
21
|
* Upstream interpolates `${APP_NAME}` into the `quit` description; `APP_NAME`
|
|
@@ -12,32 +12,21 @@
|
|
|
12
12
|
// below to match and adjust the count note.
|
|
13
13
|
import { homedir } from 'node:os';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
|
+
import { expandTilde } from '../fs-utils.js';
|
|
15
16
|
/** pi's sessions root, VENDORED from pi `config.getSessionsDir()` (= `<agentDir>/
|
|
16
17
|
* sessions`). pi's package `exports` map is `.`-only, so config.js can't be
|
|
17
18
|
* deep-imported, and a ROOT import of `getAgentDir` would eager-load the entire
|
|
18
19
|
* heavy pi SDK index on crtr's front-door hot path (the reason broker-sdk.ts
|
|
19
20
|
* dynamic-imports the engine). Mirrors pi: `PI_CODING_AGENT_DIR` env, else
|
|
20
21
|
* `~/.pi/agent` (APP_NAME defaults to 'pi'). Re-sync on a pi SDK bump that moves
|
|
21
|
-
* the sessions dir — same vendoring rationale as the slash-command list below.
|
|
22
|
+
* the sessions dir — same vendoring rationale as the slash-command list below.
|
|
23
|
+
* `PI_CODING_AGENT_DIR` goes through the same tilde expansion pi's
|
|
24
|
+
* `getAgentDir()` applies, or `~/custom-pi` yields a broken relative path. */
|
|
22
25
|
export function piSessionsRoot() {
|
|
23
26
|
const env = process.env['PI_CODING_AGENT_DIR'];
|
|
24
27
|
const agentDir = env !== undefined && env !== '' ? expandTilde(env) : join(homedir(), '.pi', 'agent');
|
|
25
28
|
return join(agentDir, 'sessions');
|
|
26
29
|
}
|
|
27
|
-
/** Mirror pi's `expandTildePath` (= `normalizePath` with the default expandTilde
|
|
28
|
-
* on): a bare `~` is the home dir, and a leading `~/` (or `~\` on win32) joins
|
|
29
|
-
* the remainder under home. pi's `getAgentDir()` runs `PI_CODING_AGENT_DIR`
|
|
30
|
-
* through this, so the vendored root must too or `~/custom-pi` yields a broken
|
|
31
|
-
* relative path. Other forms (absolute, relative, `~user`) pass through as pi
|
|
32
|
-
* leaves them. */
|
|
33
|
-
function expandTilde(p) {
|
|
34
|
-
if (p === '~')
|
|
35
|
-
return homedir();
|
|
36
|
-
if (p.startsWith('~/') || (process.platform === 'win32' && p.startsWith('~\\'))) {
|
|
37
|
-
return join(homedir(), p.slice(2));
|
|
38
|
-
}
|
|
39
|
-
return p;
|
|
40
|
-
}
|
|
41
30
|
/**
|
|
42
31
|
* pi's builtin slash commands — vendored verbatim from pi `core/slash-commands.js`
|
|
43
32
|
* `BUILTIN_SLASH_COMMANDS` (review C1). **21 entries at 0.78.1** (review n1 — NOT 23).
|
|
@@ -45,7 +34,7 @@ function expandTilde(p) {
|
|
|
45
34
|
* Builtins are NOT engine-interpreted (`session.prompt('/model')` ships `/model`
|
|
46
35
|
* to the LLM as literal text), so the broker's `get_commands` op MERGES this list
|
|
47
36
|
* with the engine's registered commands/templates/skills, and the viewer
|
|
48
|
-
* autocomplete parses these locally. Both the broker
|
|
37
|
+
* autocomplete parses these locally. Both the broker and the viewer
|
|
49
38
|
* import this single copy.
|
|
50
39
|
*
|
|
51
40
|
* Upstream interpolates `${APP_NAME}` into the `quit` description; `APP_NAME`
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// orchestrator), and seeds a roadmap scaffold.
|
|
8
8
|
// The transition guidance the node needs is injected CENTRALLY by the
|
|
9
9
|
// persona injector (runtime/persona.ts) at the turn boundary — promote()
|
|
10
|
-
// itself no
|
|
10
|
+
// itself returns no guidance and emits none by hand.
|
|
11
11
|
// 2. Refresh → persona swap (permanent). On the next fresh revive the node
|
|
12
12
|
// starts with the orchestrator system prompt baked in (because the launch
|
|
13
13
|
// spec now says orchestrator). The injected guidance bridges until then.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The re-task/reopen gate
|
|
1
|
+
// The re-task/reopen gate — enforced at every doorway that can revive a
|
|
2
2
|
// node: `node message send` immediate delivery, `node wait deadline`, and
|
|
3
3
|
// `node lifecycle revive`.
|
|
4
4
|
//
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// so the node's next real `push final` re-latches through the normal
|
|
13
13
|
// `WHERE final_report IS NULL` guard.
|
|
14
14
|
//
|
|
15
|
-
// Concurrency
|
|
15
|
+
// Concurrency: every check here reads `final_report` FRESH from
|
|
16
16
|
// the DB — never a caller-cached snapshot from an earlier point in the
|
|
17
17
|
// command — and the actual clear is a compare-and-swap (`UPDATE ... WHERE
|
|
18
18
|
// final_report = <the exact value just observed>`), mirroring the CAS
|
|
@@ -13,18 +13,18 @@ export declare const TERMINAL_BY_CHOICE: readonly NodeStatus[];
|
|
|
13
13
|
* inbox watcher's own hold-and-flush) ever brings such a node back on its
|
|
14
14
|
* own — only an explicit revive/reopen does. Shared by `isDisconnected`
|
|
15
15
|
* (mass-revive scope, below) and `node message send --tier deferred`'s terminal-edge
|
|
16
|
-
* rejection
|
|
16
|
+
* rejection — a deferred entry appended to such a target would
|
|
17
17
|
* never be delivered, so that doorway rejects instead of silently holding
|
|
18
18
|
* it forever. */
|
|
19
19
|
export declare function hasNoNaturalCycle(meta: Pick<NodeMeta, 'status' | 'final_report'>): boolean;
|
|
20
20
|
/** True when `meta`'s engine is NOT running but it has a resumable saved session
|
|
21
|
-
* — the precise "disconnected" predicate
|
|
21
|
+
* — the precise "disconnected" predicate.
|
|
22
22
|
*
|
|
23
23
|
* - engine not running: no live `pi_pid` (a dead/absent broker pid).
|
|
24
24
|
* - reconnectable: a captured pi session marker (`pi_session_file` or
|
|
25
25
|
* `pi_session_id`) — without one there is nothing to reopen. reviveNode
|
|
26
26
|
* resumes by the `.jsonl` path when it still exists, else relaunches fresh (a
|
|
27
|
-
* bare `pi_session_id` alone
|
|
27
|
+
* bare `pi_session_id` alone does not resume — the broker preflight rejects
|
|
28
28
|
* it — so such a node comes back fresh).
|
|
29
29
|
* - `human`-kind rows are the `crtr human` bridge, never a pi engine, so they
|
|
30
30
|
* are never "disconnected" (mirrors the daemon's superviseTick carve-out).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// revive-all — resume EVERY disconnected node in one shot
|
|
1
|
+
// revive-all — resume EVERY disconnected node in one shot.
|
|
2
2
|
//
|
|
3
3
|
// After a mass-disconnect event (a reboot, a killed login/tmux session, a mass
|
|
4
4
|
// crash, or the daemon being down a while) many nodes end up with their
|
|
@@ -30,20 +30,20 @@ export const TERMINAL_BY_CHOICE = ['done', 'canceled'];
|
|
|
30
30
|
* inbox watcher's own hold-and-flush) ever brings such a node back on its
|
|
31
31
|
* own — only an explicit revive/reopen does. Shared by `isDisconnected`
|
|
32
32
|
* (mass-revive scope, below) and `node message send --tier deferred`'s terminal-edge
|
|
33
|
-
* rejection
|
|
33
|
+
* rejection — a deferred entry appended to such a target would
|
|
34
34
|
* never be delivered, so that doorway rejects instead of silently holding
|
|
35
35
|
* it forever. */
|
|
36
36
|
export function hasNoNaturalCycle(meta) {
|
|
37
37
|
return meta.final_report != null || TERMINAL_BY_CHOICE.includes(meta.status);
|
|
38
38
|
}
|
|
39
39
|
/** True when `meta`'s engine is NOT running but it has a resumable saved session
|
|
40
|
-
* — the precise "disconnected" predicate
|
|
40
|
+
* — the precise "disconnected" predicate.
|
|
41
41
|
*
|
|
42
42
|
* - engine not running: no live `pi_pid` (a dead/absent broker pid).
|
|
43
43
|
* - reconnectable: a captured pi session marker (`pi_session_file` or
|
|
44
44
|
* `pi_session_id`) — without one there is nothing to reopen. reviveNode
|
|
45
45
|
* resumes by the `.jsonl` path when it still exists, else relaunches fresh (a
|
|
46
|
-
* bare `pi_session_id` alone
|
|
46
|
+
* bare `pi_session_id` alone does not resume — the broker preflight rejects
|
|
47
47
|
* it — so such a node comes back fresh).
|
|
48
48
|
* - `human`-kind rows are the `crtr human` bridge, never a pi engine, so they
|
|
49
49
|
* are never "disconnected" (mirrors the daemon's superviseTick carve-out).
|
|
@@ -78,8 +78,8 @@ export function listDisconnected() {
|
|
|
78
78
|
// IDENTITY-aware AND requires CONFIRMED alive: a bare isPidAlive would
|
|
79
79
|
// misread a recycled pi_pid as "engine running" and SKIP the node, so it
|
|
80
80
|
// never reaches reviveNode's own identity-aware double-launch guard
|
|
81
|
-
//
|
|
82
|
-
//
|
|
81
|
+
// guard. `dead` OR `indeterminate` both count as disconnected/eligible
|
|
82
|
+
// here — only a
|
|
83
83
|
// positively confirmed-alive read is treated as "still connected".
|
|
84
84
|
if (meta !== null && isDisconnected(meta, (pid) => recordedPidLiveness(pid, meta.pi_pid_identity) === 'alive'))
|
|
85
85
|
out.push(meta);
|
|
@@ -100,7 +100,7 @@ export function reviveNode(nodeId, opts) {
|
|
|
100
100
|
}
|
|
101
101
|
// Fresh durable snapshot immediately before the lifecycle guard.
|
|
102
102
|
meta = getNode(nodeId) ?? meta;
|
|
103
|
-
//
|
|
103
|
+
// Refuse to revive a LATCHED node — the reopen gate at every
|
|
104
104
|
// legitimate caller (node message send, node lifecycle revive) already checked this
|
|
105
105
|
// before calling in, but a target can finalize in the gap between that
|
|
106
106
|
// check and this call. This authoritative snapshot closes that race;
|
|
@@ -109,8 +109,7 @@ export interface SpawnChildResult {
|
|
|
109
109
|
* (explicit `--parent` or the calling node's CRTR_NODE_ID). A --root spawn
|
|
110
110
|
* does not: it is top-level by definition and the spawner identity is
|
|
111
111
|
* provenance only — a human shell with no CRTR_NODE_ID is a legitimate root
|
|
112
|
-
* spawner
|
|
113
|
-
* throw here). */
|
|
112
|
+
* spawner, so `crtr node new --root` from outside a node must not throw. */
|
|
114
113
|
export declare function resolveSpawner(parent: string | undefined, ctxNodeId: string | null, root: boolean): string | null;
|
|
115
114
|
/** Spawn a node from a live node. By default a managed terminal worker in a
|
|
116
115
|
* background window, with the spawner auto-subscribed (active) via spawnNode.
|
|
@@ -187,8 +187,7 @@ export async function assertLaunchModelRegistered(request, options, registry) {
|
|
|
187
187
|
* (explicit `--parent` or the calling node's CRTR_NODE_ID). A --root spawn
|
|
188
188
|
* does not: it is top-level by definition and the spawner identity is
|
|
189
189
|
* provenance only — a human shell with no CRTR_NODE_ID is a legitimate root
|
|
190
|
-
* spawner
|
|
191
|
-
* throw here). */
|
|
190
|
+
* spawner, so `crtr node new --root` from outside a node must not throw. */
|
|
192
191
|
export function resolveSpawner(parent, ctxNodeId, root) {
|
|
193
192
|
const spawner = parent ?? ctxNodeId ?? null;
|
|
194
193
|
if (!root && spawner === null) {
|
|
@@ -218,7 +217,7 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
218
217
|
const attendedRoot = root && lifecycle === 'resident';
|
|
219
218
|
// Born WITHOUT a name — the canvas-goal-capture extension names it async from
|
|
220
219
|
// its first message (the kickoff task) inside its own pi process, so spawn
|
|
221
|
-
// never blocks on the LLM naming round-trip (
|
|
220
|
+
// never blocks on the LLM naming round-trip (a 2-3s freeze).
|
|
222
221
|
const wantsWorktree = opts.worktree === true;
|
|
223
222
|
const nodeId = wantsWorktree ? (opts.nodeId ?? newNodeId()) : opts.nodeId;
|
|
224
223
|
// Preflight: resolve every fallible input BEFORE creating the worktree or the
|
|
@@ -3,15 +3,13 @@ import { dirname, resolve } from 'node:path';
|
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { nodeDir, contextDir } from '../canvas/paths.js';
|
|
5
5
|
import { InputError } from '../io.js';
|
|
6
|
+
import { isRecord } from '../../shared/predicates.js';
|
|
6
7
|
export function outputSchemaPath(nodeId) {
|
|
7
8
|
return `${nodeDir(nodeId)}/output-schema.json`;
|
|
8
9
|
}
|
|
9
10
|
export function outputResultPath(nodeId) {
|
|
10
11
|
return `${contextDir(nodeId)}/result.json`;
|
|
11
12
|
}
|
|
12
|
-
function isObject(value) {
|
|
13
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
14
|
-
}
|
|
15
13
|
export function parseOutputSchemaValue(value) {
|
|
16
14
|
const raw = value?.trim();
|
|
17
15
|
if (raw === undefined || raw === '')
|
|
@@ -49,7 +47,7 @@ export function parseOutputSchemaValue(value) {
|
|
|
49
47
|
next: 'Pass valid JSON whose top-level value is an object.',
|
|
50
48
|
});
|
|
51
49
|
}
|
|
52
|
-
if (!
|
|
50
|
+
if (!isRecord(parsed)) {
|
|
53
51
|
throw new InputError({
|
|
54
52
|
error: 'bad_output_schema',
|
|
55
53
|
message: `output schema from ${source} must be a JSON object`,
|
|
@@ -91,7 +89,7 @@ export function readOutputRequest(nodeId) {
|
|
|
91
89
|
catch (err) {
|
|
92
90
|
return { state: 'invalid', error: `request file is not valid JSON: ${err.message}` };
|
|
93
91
|
}
|
|
94
|
-
if (!
|
|
92
|
+
if (!isRecord(parsed)) {
|
|
95
93
|
return { state: 'invalid', error: 'request file top-level value must be an object' };
|
|
96
94
|
}
|
|
97
95
|
const mode = parsed['mode'];
|
|
@@ -99,7 +97,7 @@ export function readOutputRequest(nodeId) {
|
|
|
99
97
|
if (mode !== 'terminal' && mode !== 'oneoff') {
|
|
100
98
|
return { state: 'invalid', error: `mode must be 'terminal' or 'oneoff', got ${JSON.stringify(mode)}` };
|
|
101
99
|
}
|
|
102
|
-
if (!
|
|
100
|
+
if (!isRecord(schema)) {
|
|
103
101
|
return { state: 'invalid', error: `schema must be an object, got ${typeof schema}` };
|
|
104
102
|
}
|
|
105
103
|
return { state: 'valid', request: { mode, schema } };
|
|
@@ -7,7 +7,7 @@ import { tmpdir } from 'node:os';
|
|
|
7
7
|
import { join } from 'node:path';
|
|
8
8
|
import { ATTACH_CONTROL_BINDINGS, BINDING_CATALOG, BINDING_IDS, encodeAttachControlInput, formatGesture, isAttachPaneBinding, resolveUserKeybindingSettings, } from '../keybindings/index.js';
|
|
9
9
|
import { normalizeTmuxPassthrough } from '../config.js';
|
|
10
|
-
import { withExclusiveDirectoryLock } from '../
|
|
10
|
+
import { withExclusiveDirectoryLock } from '../exclusive-lock.js';
|
|
11
11
|
import { surfaceTmuxStyleArgs } from './surface-bg.js';
|
|
12
12
|
import { runTmux as tmux, shellQuote } from './tmux-driver.js';
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type AttachBindingId } from '../keybindings/index.js';
|
|
2
|
-
|
|
3
|
-
export
|
|
2
|
+
import { shellQuote } from '../../shared/shell-quote.js';
|
|
3
|
+
export { shellQuote };
|
|
4
4
|
declare function tmux(args: string[]): {
|
|
5
5
|
ok: boolean;
|
|
6
6
|
stdout: string;
|
|
@@ -6,13 +6,11 @@
|
|
|
6
6
|
import { execFile, spawnSync } from 'node:child_process';
|
|
7
7
|
import { encodeAttachControlInput, } from '../keybindings/index.js';
|
|
8
8
|
import { bundledPiCommandForShell } from './pi-cli.js';
|
|
9
|
+
import { shellQuote } from '../../shared/shell-quote.js';
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
|
-
//
|
|
11
|
+
// tmux invocation
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
|
-
|
|
13
|
-
export function shellQuote(s) {
|
|
14
|
-
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
15
|
-
}
|
|
13
|
+
export { shellQuote };
|
|
16
14
|
function tmux(args) {
|
|
17
15
|
const r = spawnSync('tmux', args, { encoding: 'utf8' });
|
|
18
16
|
return {
|
|
@@ -49,9 +49,10 @@ const SUMMARY_SYSTEM_PROMPT = 'Create a structured recap of this coding-agent to
|
|
|
49
49
|
'Omit inbox receipt, delivery, and queueing; node-response and report bookkeeping; help lookups; status checks; and summary-generation activity. ' +
|
|
50
50
|
'A node response may support a substantive outcome, but its arrival or resummarization is never itself an outcome. ' +
|
|
51
51
|
'If no meaningful outcome remains after those omissions, submit the single bullet "No meaningful changes." ' +
|
|
52
|
-
'Count nodes created and distinct files
|
|
52
|
+
'Count nodes created, and separately count distinct files removed from disk, only when the supplied tool arguments or results evidence them; otherwise use zero, even when the corresponding mechanics are omitted from bullets. ' +
|
|
53
|
+
'A deleted file is one that no longer exists after the segment, never a file that was merely edited or overwritten, and a recursive or wildcard removal of an unknown number of files counts as one. ' +
|
|
53
54
|
'Do not invent success when a result shows failure. ' +
|
|
54
|
-
'Call submit_summary exactly once with bullets, nodesSpawned, and
|
|
55
|
+
'Call submit_summary exactly once with bullets, nodesSpawned, and filesDeleted, then stop.';
|
|
55
56
|
const SUBMIT_SUMMARY_SCHEMA = Type.Object({
|
|
56
57
|
bullets: Type.Array(Type.String({
|
|
57
58
|
minLength: 1,
|
|
@@ -62,9 +63,9 @@ const SUBMIT_SUMMARY_SCHEMA = Type.Object({
|
|
|
62
63
|
minimum: 0,
|
|
63
64
|
description: 'Count of nodes created during the supplied segment, or zero when none are evidenced.',
|
|
64
65
|
}),
|
|
65
|
-
|
|
66
|
+
filesDeleted: Type.Integer({
|
|
66
67
|
minimum: 0,
|
|
67
|
-
description: 'Count of distinct files
|
|
68
|
+
description: 'Count of distinct files removed from disk during the supplied segment, or zero when none are evidenced.',
|
|
68
69
|
}),
|
|
69
70
|
}, { additionalProperties: false });
|
|
70
71
|
/** Generate a structured summary object, or null on every failure. */
|
|
@@ -16,10 +16,11 @@ export declare function warmPoolEnabled(): boolean;
|
|
|
16
16
|
export interface WarmRequest {
|
|
17
17
|
kind: string;
|
|
18
18
|
mode: Mode;
|
|
19
|
+
/** Where the node runs — already resolved by the create handler (pin >
|
|
20
|
+
* spawner > profile home), never a raw caller directory. */
|
|
19
21
|
cwd: string;
|
|
20
|
-
/** The
|
|
21
|
-
|
|
22
|
-
profile: string | null | undefined;
|
|
22
|
+
/** The RESOLVED profile id, or null for explicitly no profile. */
|
|
23
|
+
profileId: string | null;
|
|
23
24
|
model: string | null;
|
|
24
25
|
situationalContext: string | null;
|
|
25
26
|
name: string | null;
|
|
@@ -43,9 +44,9 @@ export declare function reapStaleSpares(): string[];
|
|
|
43
44
|
* throws and never blocks a caller: a failed mint just means the next create
|
|
44
45
|
* for this key is cold again. */
|
|
45
46
|
export declare function refillWarmPool(request: WarmRequest): void;
|
|
46
|
-
/** Mint a spare for the most recently used
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
47
|
+
/** Mint a spare for the most recently used profiles so the FIRST create after
|
|
48
|
+
* crtrd boots is warm too, not only the second. The profile is the whole
|
|
49
|
+
* recipe: it supplies the cwd, and everything else a claim reshapes live.
|
|
50
|
+
* Fire-and-forget — returns as soon as the mints are scheduled, never blocking
|
|
51
|
+
* the daemon's work lane. */
|
|
51
52
|
export declare function prewarmRecentRecipes(): void;
|