@crouton-kit/crouter 0.3.44 → 0.3.46

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.
Files changed (55) hide show
  1. package/dist/build-root.js +0 -1
  2. package/dist/builtin-memory/00-runtime-base.md +3 -0
  3. package/dist/builtin-memory/05-kinds/advisor/00-base.md +3 -2
  4. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +26 -0
  5. package/dist/builtin-memory/internal/nodes-and-canvas.md +2 -2
  6. package/dist/builtin-memory/wedged-child-on-runaway-bash.md +2 -2
  7. package/dist/builtin-views/chat/web.jsx +1 -1
  8. package/dist/clients/attach/__tests__/crtr-output-render.test.js +11 -9
  9. package/dist/clients/attach/attach-cmd.js +557 -546
  10. package/dist/clients/attach/chat-view.d.ts +4 -0
  11. package/dist/clients/attach/chat-view.js +13 -2
  12. package/dist/clients/attach/mermaid-render.d.ts +23 -0
  13. package/dist/clients/attach/mermaid-render.js +124 -0
  14. package/dist/clients/web/source-cache.js +1 -1
  15. package/dist/commands/__tests__/revive-now-gate.test.js +2 -2
  16. package/dist/commands/canvas-history/read.js +3 -3
  17. package/dist/commands/canvas-history/search.js +1 -1
  18. package/dist/commands/canvas-history.js +11 -12
  19. package/dist/commands/canvas-rebuild-index.js +2 -2
  20. package/dist/commands/canvas.js +4 -4
  21. package/dist/commands/node-inspect-artifacts.d.ts +1 -0
  22. package/dist/commands/{canvas-history/show.js → node-inspect-artifacts.js} +7 -7
  23. package/dist/commands/node-lifecycle-revive.d.ts +12 -0
  24. package/dist/commands/node-lifecycle-revive.js +166 -0
  25. package/dist/commands/node-worktree.d.ts +2 -0
  26. package/dist/commands/{worktree.js → node-worktree.js} +15 -16
  27. package/dist/commands/node.js +27 -15
  28. package/dist/commands/push.js +1 -1
  29. package/dist/commands/revive.d.ts +0 -10
  30. package/dist/commands/revive.js +31 -181
  31. package/dist/core/__tests__/kickoff.test.js +2 -2
  32. package/dist/core/__tests__/review-model-floor.test.js +16 -0
  33. package/dist/core/canvas/boot.js +1 -1
  34. package/dist/core/canvas/browse/app.js +6 -6
  35. package/dist/core/canvas/browse/render.js +1 -1
  36. package/dist/core/canvas/status-glyph.d.ts +1 -1
  37. package/dist/core/canvas/status-glyph.js +1 -1
  38. package/dist/core/preview-registry.js +27 -26
  39. package/dist/core/profiles/select.d.ts +12 -4
  40. package/dist/core/profiles/select.js +182 -22
  41. package/dist/core/runtime/bearings.js +1 -1
  42. package/dist/core/runtime/close.js +1 -1
  43. package/dist/core/runtime/kickoff.js +3 -3
  44. package/dist/core/runtime/revive.js +2 -2
  45. package/dist/core/runtime/spawn.d.ts +6 -3
  46. package/dist/core/view/stream-local.js +1 -1
  47. package/dist/core/worktree.js +4 -4
  48. package/dist/daemon/crtrd.js +3 -3
  49. package/dist/web/transport-stream.js +1 -1
  50. package/dist/web-client/assets/{index-B00YpRQ1.js → index-Di-gSsVn.js} +2 -2
  51. package/dist/web-client/index.html +1 -1
  52. package/package.json +2 -2
  53. package/scripts/postinstall.mjs +73 -0
  54. package/dist/commands/canvas-history/show.d.ts +0 -1
  55. package/dist/commands/worktree.d.ts +0 -2
@@ -18,7 +18,6 @@ const SUBTREE_LOADERS = {
18
18
  push: async () => (await import('./commands/push.js')).registerPush(),
19
19
  canvas: async () => (await import('./commands/canvas.js')).registerCanvas(),
20
20
  surface: async () => (await import('./commands/surface.js')).registerSurface(),
21
- worktree: async () => (await import('./commands/worktree.js')).registerWorktree(),
22
21
  };
23
22
  /** Every shipped subtree name. Cheap (no module loading) — the front-door
24
23
  * recursion guard and the dispatcher's first-token routing need only names. */
@@ -38,5 +38,8 @@ Two different moves; don't conflate them. **Promote** when the *shape* of the jo
38
38
 
39
39
  Don't promote or yield for work that fits one window — finish it with `crtr push final`.
40
40
 
41
+ ## Diagrams
42
+ When structure would land faster as a picture than as prose — a flow, a state machine, a small architecture — write it as a ```mermaid fenced code block. The human's viewer renders it inline as a terminal diagram. Reach for it when the shape is the point, not for everything.
43
+
41
44
  ## When you are blocked on a future event or time
42
45
  You are not stuck and you are not done — you are waiting. Don't busy-loop or finish: just stop and go dormant, and the runtime wakes you when the thing you wait on happens. See *Waiting* for the details — including the one case where you instead schedule the wake yourself.
@@ -7,8 +7,9 @@ gate: {kind: advisor}
7
7
  rationale: >-
8
8
  the reciprocal of explore's model-tier economics — judgment, debugging, and
9
9
  tradeoff work need the slow/expensive think-y tier, so it is fenced into its own kind
10
- rather than left as a per-task instruction. No orchestrator variant on purpose: you
11
- usually just want the opinion spawn a few advisors, not a fan-out.
10
+ rather than left as a per-task instruction. The orchestrator variant is the council:
11
+ reach for it only when the call is consequential enough to fund deliberation the
12
+ ordinary case is still one advisor (or a few, un-orchestrated), not a fan-out.
12
13
  ---
13
14
 
14
15
  You are an advisor agent: a senior debugging and engineering judgment partner. Your work is diagnosis, explanation, tradeoff analysis, and recommended next action.
@@ -0,0 +1,26 @@
1
+ ---
2
+ kind: preference
3
+ when-and-why-to-read: When a node is spawned as kind advisor in orchestrator mode, this preference should be read because it defines how to convene a council of advisors and synthesize one verdict without the debate-to-consensus trap.
4
+ system-prompt-visibility: content
5
+ file-read-visibility: none
6
+ gate: {kind: advisor, mode: orchestrator}
7
+ rationale: >-
8
+ Everyone's instinct for getting the right answer out of a council is to make the agents
9
+ debate until they agree — and that instinct is empirically backwards. Agreement is
10
+ manufactured by conformity (debate flips correct answers to wrong at rates up to 85.5%);
11
+ the correctness lives in blind independence, cross-family decorrelation, and
12
+ confidence-weighted synthesis. Without this persona an orchestrator runs a persuasion
13
+ contest and ships its overconfident output as a verdict.
14
+ ---
15
+
16
+ You are an **advisor council orchestrator** — you convene several senior advisors on one consequential judgment call and deliver a single verdict. Reach for the council only when being wrong is expensive enough to fund the deliberation; for an ordinary second opinion, a single advisor (or a few, un-orchestrated) is the right spend.
17
+
18
+ **Round 1 is blind, parallel, and decorrelated.** Spawn 3–5 advisors at once; no member sees another's work — first-round independence is the single highest-leverage choice, because it defeats the cascades, sycophancy, and correlated-error inflation that peer exposure manufactures. Decorrelate deliberately: mix model families across members — pin cross-family members to a different ladder at spawn (`crtr node new --model openai/ultra`, against the default anthropic ultra) — since cross-family heterogeneity is the strongest decorrelation lever there is; correlated members collapse a nine-seat panel to two effective votes, and model diversity beats persona variety. On top of family mixing, give each the same question under a distinct frame — different evidence emphases, priors to steelman, decompositions. Every member returns: recommendation, a calibrated numeric confidence — the percent probability that its recommendation is correct given the available evidence, so weights are comparable across members — its strongest private reason, and its key evidence.
19
+
20
+ **Synthesize as a chairman, not a vote.** Weight by stated confidence and evidence quality — confidence-weighting is what lets the council drift toward the correct answer instead of the popular one — reconcile and flag conflicts without inventing, and preserve each member's original answer so a well-argued minority is never erased. Treat unanimity as an alarm to check for correlated error or false convergence, never as proof. Don't hedge toward the average; independent aggregates run under-confident, so commit to the best-supported answer.
21
+
22
+ **Run a fresh pre-mortem gate every time.** Before finalizing, hand the draft verdict to one clean-context advisor under prospective-hindsight framing — "this was adopted and it failed; explain why" — because assigned dissent inside the group becomes theater and only a fresh critic catches what the authors can't see; prefer a different family than the members that dominated the draft. Fold the real findings in or name them as accepted risks.
23
+
24
+ **Open a second round only on a named crux, and only when it's checkable.** If members genuinely disagree, name the crux first. A verifiable crux (code, tests, docs, quotable evidence) earns one targeted adversarial round — feed back only anonymized claim/reason/evidence snippets bearing on the crux, with author, model, and conclusion labels stripped so no conformity or brand cues survive, and have members attack and verify those specific claims. An unverifiable crux (judgment, taste, values) gets no persuasion contest: the more persuasive member wins regardless of correctness, so you adjudicate it or surface the tradeoff to the owner as a named disagreement. Cap at ~2 rounds and stop when positions stabilize — extra rounds buy conformity, not accuracy.
25
+
26
+ Members contribute judgment only: they are read-only opinion sources, and you are the single synthesizer and the only writer of the verdict. Your deliverable is one verdict, conclusion first — recommendation and confidence, the strongest opposing case and why it lost, and any residual disagreement reported *as* disagreement with its crux named, never dissolved into false consensus and never forwarded as raw member output.
@@ -44,6 +44,6 @@ Tear-down: `node lifecycle close` cascade-cancels a node + its exclusive subtree
44
44
 
45
45
  ## Revive & the daemon
46
46
 
47
- A dormant node (done/idle/dead/canceled) is reopened with `canvas revive` (resumes the saved conversation, or `--fresh` to restart clean). `reviveNode()` is the **only** sanctioned launcher of a node's broker engine — it builds the pi invocation, sets `CRTR_NODE_ID` + canvas extensions, runs `transition('revive')`, and starts the headless broker host, keeping the db row and broker process in lockstep. Never spawn `pi --session` raw, and never open a node by spawning pi directly — UIs go through `node focus` / `canvas revive`.
47
+ A dormant node (done/idle/dead/canceled) is reopened with `node lifecycle revive <id>` (resumes the saved conversation, or `--fresh` to restart clean); mass-reconnecting EVERY disconnected node at once (a reboot/crash recovery) is `canvas revive --all` instead. `reviveNode()` is the **only** sanctioned launcher of a node's broker engine — it builds the pi invocation, sets `CRTR_NODE_ID` + canvas extensions, runs `transition('revive')`, and starts the headless broker host, keeping the db row and broker process in lockstep. Never spawn `pi --session` raw, and never open a node by spawning pi directly — UIs go through `node focus` / `node lifecycle revive`.
48
48
 
49
- The **daemon** (`crtrd`, managed via `canvas daemon start/stop/status`) is a thin supervisor: it polls broker liveness via `pi_pid` and auto-revives active/idle nodes whose broker exited. It does NOT host agents, open viewers, or auto-revive *canceled* nodes (reach for `canvas revive` for those). After rebuilding crouter's `dist/`, restart the daemon — it loads compiled code at start and never reloads it. Restarting is safe: it never signals running nodes.
49
+ The **daemon** (`crtrd`, managed via `canvas daemon start/stop/status`) is a thin supervisor: it polls broker liveness via `pi_pid` and auto-revives active/idle nodes whose broker exited. It does NOT host agents, open viewers, or auto-revive *canceled* nodes (reach for `node lifecycle revive` for those). After rebuilding crouter's `dist/`, restart the daemon — it loads compiled code at start and never reloads it. Restarting is safe: it never signals running nodes.
@@ -13,7 +13,7 @@ A base node can wedge INDEFINITELY on a single runaway bash command (classic: `g
13
13
  **Remediation branches on whether the tree has a descendant (issue #119):**
14
14
 
15
15
  - **A subprocess exists (the common case)** — a runaway bash/find/grep is the likely cause. The daemon does NOT touch the engine here; you kill the subprocess yourself (see below).
16
- - **No subprocess exists** — the broker pid stands alone in its own tree (the engine itself stalled, e.g. mid a `model_change`, with nothing a human could kill). The daemon performs the ONLY remaining remediation itself: it SIGTERMs the broker (the same on-demand kick `canvas revive --now` does), and its own dead-pid crash-grace path resumes it on the saved session within ~20s, no lost context. The resume alone can leave the node IDLE rather than mid-turn (it reloads the session but doesn't restart the dead turn) — if telemetry stays frozen after the kick, nudge it (see "No-subprocess case" below).
16
+ - **No subprocess exists** — the broker pid stands alone in its own tree (the engine itself stalled, e.g. mid a `model_change`, with nothing a human could kill). The daemon performs the ONLY remaining remediation itself: it SIGTERMs the broker (the same on-demand kick `node lifecycle revive --now` does), and its own dead-pid crash-grace path resumes it on the saved session within ~20s, no lost context. The resume alone can leave the node IDLE rather than mid-turn (it reloads the session but doesn't restart the dead turn) — if telemetry stays frozen after the kick, nudge it (see "No-subprocess case" below).
17
17
 
18
18
  **Why it used to be invisible to the orchestrator:** the wake spine only fires on a child's push/finalize/crash. A child wedged *mid-turn* does none of those — its row stays `status=active` and the dashboard shows it `●` working. Before #110, the parent got no wake at all, so an entire wave could stall for hours looking healthy; cli-verbs-B2 sat wedged ~2h14m on a `grep /` before a safety-deadline inspection caught it. The daemon-side detection above closes that gap; you should no longer need to notice a wedge by elapsed-time vibes alone — but if you do (the daemon hasn't caught up yet, or the wedge is younger than its grace window), the manual inspection below still works.
19
19
 
@@ -30,5 +30,5 @@ The wedge can outlive any subprocess, or exist with none at all. A node (includi
30
30
  **This is now the daemon's own remediation (issue #119) — it auto-kicks the broker for you.** If it hasn't caught up yet (younger than the wedge grace, or the daemon was down), do it manually:
31
31
 
32
32
  1. `kill -TERM <broker_pid>` — the wedged broker. Confirm it dies (`ps -p <pid>`) and that no orphan subprocess lingers (`ps -o pid,ppid,command | awk '$2==<pid>'`).
33
- 2. The **daemon auto-resumes** it within ~20s on the saved session — a fresh broker pid appears (`node inspect show`), `status=active`, `intent=null`, `view.sock` accepts connections. (No manual `canvas revive` needed if the daemon is up; do it manually only if it doesn't come back.)
33
+ 2. The **daemon auto-resumes** it within ~20s on the saved session — a fresh broker pid appears (`node inspect show`), `status=active`, `intent=null`, `view.sock` accepts connections. (No manual `node lifecycle revive` needed if the daemon is up; do it manually only if it doesn't come back.)
34
34
  3. **The resume alone leaves it IDLE** — it loads the session but does not restart the dead turn, so telemetry stays frozen and it processes nothing. Send a `--tier critical` `node msg` to force a fresh turn (critical = interrupt + new turn). Word it as a recovery notice: tell it the broker was wedged, to re-orient from `context/roadmap.md`/feed, and NOT to trust that recent inbox context survived (cursor may have advanced past messages the wedged engine never folded into the conversation). Confirm success by watching `telemetry.json` `updated_at` go current and the node run bash again.
@@ -129,7 +129,7 @@ function connectionGuidance(state) {
129
129
  Comp: NotReady,
130
130
  headline: 'node is asleep',
131
131
  explanation: 'Focus or revive the node first, then reconnect.',
132
- nextStep: `crtr node focus ${target} · or crtr canvas revive ${target}`,
132
+ nextStep: `crtr node focus ${target} · or crtr node lifecycle revive ${target}`,
133
133
  };
134
134
  case 'no-node':
135
135
  return {
@@ -745,8 +745,8 @@ test('a crtr canvas attention map card collapses to a hottest-id digest, PASS wh
745
745
  .join('\n');
746
746
  assert.match(expanded, /counts: \{"n1":0,"n2":3,"n3":5\}/);
747
747
  });
748
- test('a crtr canvas revive card collapses to a kicked/revived status line, argv-recovered id', () => {
749
- const kickedDef = createCrtrBashToolDefinition({ command: 'crtr canvas revive mq2jpqz5-87fa626e --now' });
748
+ test('a crtr node lifecycle revive card collapses to a kicked/revived status line, argv-recovered id', () => {
749
+ const kickedDef = createCrtrBashToolDefinition({ command: 'crtr node lifecycle revive mq2jpqz5-87fa626e --now' });
750
750
  const kicked = kickedDef
751
751
  .renderResult({ content: [{ type: 'text', text: ['- session: mysession', '- kicked: true', '', 'Sent SIGTERM to worker-a (mq2jpqz5-87fa626e, pid 456).'].join('\n') }] }, { expanded: false }, theme, {})
752
752
  .render(120)
@@ -757,7 +757,7 @@ test('a crtr canvas revive card collapses to a kicked/revived status line, argv-
757
757
  .render(120)
758
758
  .join('\n');
759
759
  assert.match(kickedExpanded, /Sent SIGTERM to worker-a \(mq2jpqz5-87fa626e, pid 456\)\./);
760
- const revivedDef = createCrtrBashToolDefinition({ command: 'crtr canvas revive mq2jpqz5-87fa626e' });
760
+ const revivedDef = createCrtrBashToolDefinition({ command: 'crtr node lifecycle revive mq2jpqz5-87fa626e' });
761
761
  const revived = revivedDef
762
762
  .renderResult({ content: [{ type: 'text', text: ['- session: mysession', '- resumed: true', '- ready: true'].join('\n') }] }, { expanded: false }, theme, {})
763
763
  .render(120)
@@ -780,9 +780,11 @@ test('a crtr canvas revive card collapses to a kicked/revived status line, argv-
780
780
  .join('\n');
781
781
  assert.match(notReadyExpanded, /resumed: false/);
782
782
  assert.match(notReadyExpanded, /ready: false/);
783
- // `--all` carries a `- mode:` bullet and is deliberately skipped (null) —
784
- // falls through to generic truncation, raw `mode` text survives.
785
- const allPreview = revivedDef
783
+ // `canvas revive --all` carries a `- mode:` bullet; no summarizer is
784
+ // registered for it (rare admin path) falls through to generic
785
+ // truncation, raw `mode` text survives.
786
+ const allDef = createCrtrBashToolDefinition({ command: 'crtr canvas revive --all' });
787
+ const allPreview = allDef
786
788
  .renderResult({ content: [{ type: 'text', text: '- mode: preview\n- candidates: mq1' }] }, { expanded: false }, theme, {})
787
789
  .render(120)
788
790
  .join('\n');
@@ -907,10 +909,10 @@ const HISTORY_SHOW_OUTPUT = [
907
909
  '| n1:docs/design.md | doc | 2026-01-03T00:00:00.000Z | Design doc |',
908
910
  '| n1:roadmap.md | roadmap | 2026-01-04T00:00:00.000Z | Roadmap |',
909
911
  '',
910
- 'Read one with `canvas history read <ref>`; reopen the node with `canvas revive n1`.',
912
+ 'Read one with `canvas history read <ref>`; reopen the node with `node lifecycle revive n1`.',
911
913
  ].join('\n');
912
- test('a crtr canvas history show card collapses to a node line + type-count digest, not the raw table', () => {
913
- const def = createCrtrBashToolDefinition({ command: 'crtr canvas history show n1' });
914
+ test('a crtr node inspect artifacts card collapses to a node line + type-count digest, not the raw table', () => {
915
+ const def = createCrtrBashToolDefinition({ command: 'crtr node inspect artifacts n1' });
914
916
  const collapsed = def
915
917
  .renderResult({ content: [{ type: 'text', text: HISTORY_SHOW_OUTPUT }] }, { expanded: false }, theme, {})
916
918
  .render(120)