@crouton-kit/crouter 0.3.28 → 0.3.30
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 +15 -7
- package/dist/build-root.js +48 -34
- package/dist/builtin-memory/crouter-development/marketplaces.md +7 -6
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +1 -1
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +1 -1
- package/dist/builtin-memory/crouter-development/personas.md +1 -1
- package/dist/builtin-memory/crouter-development/plugins.md +20 -18
- package/dist/builtin-memory/design.md +1 -1
- package/dist/builtin-memory/development.md +1 -1
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
- package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/planning.md +1 -1
- package/dist/builtin-memory/spec.md +6 -4
- package/dist/builtin-personas/orchestration-kernel.md +4 -5
- package/dist/builtin-personas/runtime-base.md +5 -4
- package/dist/builtin-personas/spec/PERSONA.md +8 -4
- package/dist/builtin-personas/spec/orchestrator.md +5 -3
- package/dist/builtin-personas/spec/requirements/PERSONA.md +10 -0
- package/dist/builtin-views/canvas/core.mjs +586 -0
- package/dist/builtin-views/canvas/text.mjs +58 -0
- package/dist/builtin-views/canvas/tui.mjs +165 -0
- package/dist/builtin-views/canvas/web.jsx +120 -0
- package/dist/builtin-views/git-pr/core.mjs +673 -0
- package/dist/builtin-views/git-pr/text.mjs +84 -0
- package/dist/builtin-views/git-pr/tui.mjs +302 -0
- package/dist/builtin-views/git-pr/web.jsx +216 -0
- package/dist/builtin-views/inbox/_lib/render.mjs +1 -3
- package/dist/builtin-views/inbox/core.mjs +1273 -0
- package/dist/builtin-views/inbox/text.mjs +73 -0
- package/dist/builtin-views/inbox/tui.mjs +312 -0
- package/dist/builtin-views/inbox/web.jsx +188 -0
- package/dist/builtin-views/linkedin/core.mjs +906 -0
- package/dist/builtin-views/linkedin/text.mjs +69 -0
- package/dist/builtin-views/linkedin/tui.mjs +427 -0
- package/dist/builtin-views/linkedin/web.jsx +206 -0
- package/dist/builtin-views/workspace-sidebar/core.mjs +621 -0
- package/dist/builtin-views/workspace-sidebar/text.mjs +53 -0
- package/dist/builtin-views/workspace-sidebar/tui.mjs +142 -0
- package/dist/builtin-views/workspace-sidebar/web.jsx +109 -0
- package/dist/cli.js +22 -17
- package/dist/clients/attach/__tests__/action-parity.test.js +48 -0
- package/dist/clients/attach/__tests__/clipboard-image.test.js +76 -0
- package/dist/clients/attach/__tests__/editor-newline.test.js +57 -0
- package/dist/clients/attach/__tests__/git-info.test.js +31 -0
- package/dist/clients/attach/__tests__/onrequest-wired.test.js +20 -0
- package/dist/clients/attach/__tests__/slash-quit-copy.test.js +42 -0
- package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
- package/dist/clients/attach/attach-cmd.js +282 -46
- package/dist/clients/attach/auth-pickers.d.ts +9 -0
- package/dist/clients/attach/auth-pickers.js +194 -0
- package/dist/clients/attach/canvas-panels.d.ts +4 -2
- package/dist/clients/attach/canvas-panels.js +45 -17
- package/dist/clients/attach/chat-view.d.ts +27 -2
- package/dist/clients/attach/chat-view.js +98 -9
- package/dist/clients/attach/clipboard-image.d.ts +47 -8
- package/dist/clients/attach/clipboard-image.js +150 -26
- package/dist/clients/attach/clipboard-text.d.ts +3 -0
- package/dist/clients/attach/clipboard-text.js +39 -0
- package/dist/clients/attach/config-load.d.ts +54 -7
- package/dist/clients/attach/config-load.js +99 -3
- package/dist/clients/attach/context-message.d.ts +12 -0
- package/dist/clients/attach/context-message.js +72 -0
- package/dist/clients/attach/git-info.d.ts +20 -0
- package/dist/clients/attach/git-info.js +44 -0
- package/dist/clients/attach/graph-overlay.d.ts +13 -4
- package/dist/clients/attach/graph-overlay.js +59 -19
- package/dist/clients/attach/input-controller.d.ts +75 -13
- package/dist/clients/attach/input-controller.js +305 -67
- package/dist/clients/attach/pickers.d.ts +58 -0
- package/dist/clients/attach/pickers.js +171 -0
- package/dist/clients/attach/slash-commands.d.ts +22 -0
- package/dist/clients/attach/slash-commands.js +91 -25
- package/dist/clients/attach/titled-editor.d.ts +33 -0
- package/dist/clients/attach/titled-editor.js +84 -0
- package/dist/clients/attach/view-socket.d.ts +18 -1
- package/dist/clients/attach/view-socket.js +70 -1
- package/dist/clients/web/dev-server.d.ts +7 -0
- package/dist/clients/web/dev-server.js +59 -0
- package/dist/clients/web/events.d.ts +14 -0
- package/dist/clients/web/events.js +151 -0
- package/dist/clients/web/server.d.ts +18 -0
- package/dist/clients/web/server.js +450 -0
- package/dist/clients/web/web-cmd.d.ts +2 -0
- package/dist/clients/web/web-cmd.js +120 -0
- package/dist/commands/canvas.js +1 -2
- package/dist/commands/chord.js +4 -25
- package/dist/commands/dashboard.js +5 -1
- package/dist/commands/memory/__tests__/lint-schema.test.js +6 -6
- package/dist/commands/memory/lint.js +14 -2
- package/dist/commands/memory/read.js +2 -2
- package/dist/commands/memory/shared.d.ts +7 -1
- package/dist/commands/memory/shared.js +18 -3
- package/dist/commands/memory/write.js +8 -4
- package/dist/commands/memory.js +4 -4
- package/dist/commands/node.d.ts +1 -1
- package/dist/commands/node.js +64 -65
- package/dist/commands/pkg.js +1 -2
- package/dist/commands/revive.js +11 -3
- package/dist/commands/sys/sync.d.ts +1 -0
- package/dist/commands/sys/sync.js +187 -0
- package/dist/commands/sys.js +3 -2
- package/dist/commands/view-cycle.js +2 -2
- package/dist/commands/view-list.js +8 -8
- package/dist/commands/view-new.js +21 -17
- package/dist/commands/view-pick.js +1 -1
- package/dist/commands/view-run.js +35 -14
- package/dist/commands/view.js +8 -6
- package/dist/commands/workspace.d.ts +2 -0
- package/dist/commands/workspace.js +161 -0
- package/dist/core/__tests__/broker-double-spawn.test.d.ts +1 -0
- package/dist/core/__tests__/broker-double-spawn.test.js +143 -0
- package/dist/core/__tests__/broker-fork-seam.test.d.ts +1 -0
- package/dist/core/__tests__/broker-fork-seam.test.js +102 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +2 -0
- package/dist/core/__tests__/child-death-wake.test.js +56 -111
- package/dist/core/__tests__/close.test.js +14 -20
- package/dist/core/__tests__/context-intro.test.js +19 -19
- package/dist/core/__tests__/daemon-boot.test.js +30 -23
- package/dist/core/__tests__/draw-style.test.js +1 -1
- package/dist/core/__tests__/error-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/error-stall-recycle.test.js +141 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +17 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +22 -2
- package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
- package/dist/core/__tests__/focuses.test.js +25 -13
- package/dist/core/__tests__/fork.test.js +22 -5
- package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +57 -0
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +1 -0
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +80 -0
- package/dist/core/__tests__/full/broker-pane-resolution.test.js +90 -0
- package/dist/core/__tests__/full/cascade-close.test.js +22 -8
- package/dist/core/__tests__/full/detach-focus.test.js +66 -169
- package/dist/core/__tests__/helpers/harness.d.ts +6 -9
- package/dist/core/__tests__/helpers/harness.js +19 -36
- package/dist/core/__tests__/human-node-not-supervised.test.d.ts +1 -0
- package/dist/core/__tests__/human-node-not-supervised.test.js +83 -0
- package/dist/core/__tests__/lifecycle.test.js +1 -19
- package/dist/core/__tests__/listing-completeness.test.js +2 -2
- package/dist/core/__tests__/live-mutation-verbs.test.js +5 -5
- package/dist/core/__tests__/memory-resolver.test.js +1 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +2 -2
- package/dist/core/__tests__/refresh-stall-recycle.test.d.ts +1 -0
- package/dist/core/__tests__/refresh-stall-recycle.test.js +88 -0
- package/dist/core/__tests__/relaunch-root.test.d.ts +1 -0
- package/dist/core/__tests__/relaunch-root.test.js +114 -0
- package/dist/core/__tests__/reset.test.js +19 -79
- package/dist/core/__tests__/steer-note.test.js +9 -7
- package/dist/core/__tests__/tmux-surface.test.js +13 -7
- package/dist/core/bootstrap.d.ts +0 -3
- package/dist/core/bootstrap.js +1 -143
- package/dist/core/canvas/browse/__tests__/model.test.js +86 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +2 -1
- package/dist/core/canvas/browse/app.js +131 -8
- package/dist/core/canvas/browse/model.d.ts +23 -5
- package/dist/core/canvas/browse/model.js +68 -13
- package/dist/core/canvas/browse/render.d.ts +7 -1
- package/dist/core/canvas/browse/render.js +178 -47
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/canvas/focuses.d.ts +4 -4
- package/dist/core/canvas/focuses.js +7 -7
- package/dist/core/canvas/nav-model.js +8 -7
- package/dist/core/canvas/render.d.ts +68 -9
- package/dist/core/canvas/render.js +195 -61
- package/dist/core/canvas/types.d.ts +16 -23
- package/dist/core/config.js +1 -4
- package/dist/core/memory-resolver.js +43 -5
- package/dist/core/runtime/bearings.d.ts +1 -1
- package/dist/core/runtime/bearings.js +8 -8
- package/dist/core/runtime/branded-host.d.ts +16 -0
- package/dist/core/runtime/branded-host.js +127 -0
- package/dist/core/runtime/broker-protocol.d.ts +212 -6
- package/dist/core/runtime/broker.d.ts +4 -1
- package/dist/core/runtime/broker.js +494 -58
- package/dist/core/runtime/close.d.ts +12 -2
- package/dist/core/runtime/close.js +37 -16
- package/dist/core/runtime/front-door.js +6 -14
- package/dist/core/runtime/host.d.ts +14 -34
- package/dist/core/runtime/host.js +14 -50
- package/dist/core/runtime/launch.d.ts +12 -9
- package/dist/core/runtime/launch.js +27 -15
- package/dist/core/runtime/lifecycle.d.ts +1 -1
- package/dist/core/runtime/lifecycle.js +15 -19
- package/dist/core/runtime/nodes.d.ts +0 -55
- package/dist/core/runtime/nodes.js +6 -74
- package/dist/core/runtime/placement.d.ts +91 -335
- package/dist/core/runtime/placement.js +291 -858
- package/dist/core/runtime/promote.d.ts +2 -0
- package/dist/core/runtime/promote.js +24 -6
- package/dist/core/runtime/recap.d.ts +8 -0
- package/dist/core/runtime/recap.js +107 -0
- package/dist/core/runtime/recycle.js +25 -61
- package/dist/core/runtime/reset.d.ts +43 -37
- package/dist/core/runtime/reset.js +131 -218
- package/dist/core/runtime/revive.d.ts +9 -29
- package/dist/core/runtime/revive.js +40 -139
- package/dist/core/runtime/spawn.d.ts +15 -16
- package/dist/core/runtime/spawn.js +165 -194
- package/dist/core/runtime/surface-bg.d.ts +11 -0
- package/dist/core/runtime/surface-bg.js +68 -0
- package/dist/core/runtime/tmux.d.ts +15 -52
- package/dist/core/runtime/tmux.js +35 -99
- package/dist/core/scope.d.ts +1 -2
- package/dist/core/scope.js +1 -5
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts +1 -0
- package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +57 -0
- package/dist/core/skill-sync/builtins.d.ts +42 -0
- package/dist/core/skill-sync/builtins.js +112 -0
- package/dist/core/skill-sync/claude-plugins.d.ts +23 -0
- package/dist/core/skill-sync/claude-plugins.js +71 -0
- package/dist/core/skill-sync/engine.d.ts +42 -0
- package/dist/core/skill-sync/engine.js +633 -0
- package/dist/core/skill-sync/export.d.ts +23 -0
- package/dist/core/skill-sync/export.js +86 -0
- package/dist/core/skill-sync/manifest.d.ts +64 -0
- package/dist/core/skill-sync/manifest.js +181 -0
- package/dist/core/skill-sync/profile.d.ts +76 -0
- package/dist/core/skill-sync/profile.js +173 -0
- package/dist/core/skill-sync/snapshot.d.ts +57 -0
- package/dist/core/skill-sync/snapshot.js +120 -0
- package/dist/core/substrate/index.d.ts +1 -1
- package/dist/core/substrate/index.js +1 -1
- package/dist/core/substrate/injected-store.js +3 -3
- package/dist/core/substrate/on-read.js +68 -6
- package/dist/core/substrate/render.d.ts +8 -11
- package/dist/core/substrate/render.js +29 -43
- package/dist/core/substrate/schema.d.ts +10 -3
- package/dist/core/substrate/schema.js +6 -3
- package/dist/core/tui/host.d.ts +12 -4
- package/dist/core/tui/host.js +280 -149
- package/dist/core/view/bridge.d.ts +10 -0
- package/dist/core/view/bridge.js +31 -0
- package/dist/core/view/chrome.d.ts +9 -0
- package/dist/core/view/chrome.js +22 -0
- package/dist/core/view/contract.d.ts +171 -0
- package/dist/core/view/contract.js +23 -0
- package/dist/core/view/loader.d.ts +31 -0
- package/dist/core/view/loader.js +188 -0
- package/dist/core/view/transport-local.d.ts +7 -0
- package/dist/core/view/transport-local.js +70 -0
- package/dist/core/view/transport.d.ts +4 -0
- package/dist/core/view/transport.js +15 -0
- package/dist/daemon/crtrd.d.ts +38 -27
- package/dist/daemon/crtrd.js +303 -376
- package/dist/daemon/manage.js +6 -1
- package/dist/index.js +1 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +28 -20
- package/dist/pi-extensions/canvas-context-intro.js +4 -4
- package/dist/pi-extensions/canvas-doc-substrate.js +16 -16
- package/dist/pi-extensions/canvas-nav.js +5 -0
- package/dist/pi-extensions/canvas-recap.d.ts +37 -0
- package/dist/pi-extensions/canvas-recap.js +236 -0
- package/dist/pi-extensions/canvas-resume.js +2 -1
- package/dist/pi-extensions/canvas-stophook.d.ts +14 -12
- package/dist/pi-extensions/canvas-stophook.js +87 -101
- package/dist/pi-extensions/canvas-view.js +2 -1
- package/dist/pi-extensions/widget-order-bus.d.ts +6 -0
- package/dist/pi-extensions/widget-order-bus.js +34 -0
- package/dist/prompts/view.d.ts +2 -2
- package/dist/prompts/view.js +145 -73
- package/dist/types.d.ts +1 -6
- package/dist/types.js +1 -3
- package/dist/web/ViewChrome.d.ts +7 -0
- package/dist/web/ViewChrome.js +28 -0
- package/dist/web/ViewPane.d.ts +39 -0
- package/dist/web/ViewPane.js +48 -0
- package/dist/web/index.d.ts +6 -0
- package/dist/web/index.js +16 -0
- package/dist/web/runtime.d.ts +39 -0
- package/dist/web/runtime.js +133 -0
- package/dist/web/states.d.ts +24 -0
- package/dist/web/states.js +24 -0
- package/dist/web/transport-http.d.ts +5 -0
- package/dist/web/transport-http.js +28 -0
- package/dist/web-client/assets/index-BUvQb4hR.css +2 -0
- package/dist/web-client/assets/index-ClLQXYAE.js +10 -0
- package/dist/web-client/index.html +13 -0
- package/package.json +20 -6
- package/dist/builtin-views/canvas/client.mjs +0 -303
- package/dist/builtin-views/canvas/view.mjs +0 -576
- package/dist/builtin-views/git-pr/client.mjs +0 -440
- package/dist/builtin-views/git-pr/view.mjs +0 -675
- package/dist/builtin-views/inbox/sources/gmail.mjs +0 -965
- package/dist/builtin-views/inbox/sources/linkedin.mjs +0 -427
- package/dist/builtin-views/inbox/view.mjs +0 -889
- package/dist/builtin-views/linkedin/client.mjs +0 -610
- package/dist/builtin-views/linkedin/view.mjs +0 -1171
- package/dist/commands/pkg/bridge.d.ts +0 -1
- package/dist/commands/pkg/bridge.js +0 -137
- package/dist/commands/skill/author.d.ts +0 -3
- package/dist/commands/skill/author.js +0 -140
- package/dist/commands/skill/shared.d.ts +0 -3
- package/dist/commands/skill/shared.js +0 -19
- package/dist/commands/skill.d.ts +0 -2
- package/dist/commands/skill.js +0 -21
- package/dist/commands/tmux-spread.d.ts +0 -2
- package/dist/commands/tmux-spread.js +0 -144
- package/dist/core/__tests__/full/daemon-liveness-pane.full.test.js +0 -292
- package/dist/core/__tests__/full/placement-focus.test.js +0 -309
- package/dist/core/__tests__/full/placement-reconcile.test.js +0 -212
- package/dist/core/__tests__/full/placement-revive.test.js +0 -238
- package/dist/core/__tests__/full/placement-teardown.test.js +0 -297
- package/dist/core/__tests__/home-session.test.js +0 -186
- package/dist/core/__tests__/relaunch.test.js +0 -335
- package/dist/core/bridge-map.d.ts +0 -19
- package/dist/core/bridge-map.js +0 -73
- package/dist/core/tui/contract.d.ts +0 -83
- package/dist/core/tui/contract.js +0 -8
- package/dist/core/tui/loader.d.ts +0 -16
- package/dist/core/tui/loader.js +0 -94
- package/dist/prompts/skill.d.ts +0 -2
- package/dist/prompts/skill.js +0 -650
- /package/dist/{core/__tests__/full/daemon-liveness-pane.full.test.d.ts → clients/attach/__tests__/action-parity.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-focus.test.d.ts → clients/attach/__tests__/clipboard-image.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-reconcile.test.d.ts → clients/attach/__tests__/editor-newline.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-revive.test.d.ts → clients/attach/__tests__/git-info.test.d.ts} +0 -0
- /package/dist/{core/__tests__/full/placement-teardown.test.d.ts → clients/attach/__tests__/onrequest-wired.test.d.ts} +0 -0
- /package/dist/{core/__tests__/home-session.test.d.ts → clients/attach/__tests__/slash-quit-copy.test.d.ts} +0 -0
- /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/titled-editor.test.d.ts} +0 -0
package/dist/commands/node.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
// the spine (`focus`/`msg`). The push/feed half lives under `crtr push`.
|
|
7
7
|
import { defineLeaf, defineBranch } from '../core/command.js';
|
|
8
8
|
import { InputError } from '../core/io.js';
|
|
9
|
-
import { readConfig } from '../core/config.js';
|
|
10
9
|
import { spawnChild } from '../core/runtime/spawn.js';
|
|
11
10
|
import { promote, requestYield } from '../core/runtime/promote.js';
|
|
12
11
|
import { writeYieldMessage, readGoal } from '../core/runtime/kickoff.js';
|
|
@@ -99,7 +98,7 @@ function callerSubPersonasBlock() {
|
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
100
|
// ---------------------------------------------------------------------------
|
|
102
|
-
// node new — spawn a terminal worker as a
|
|
101
|
+
// node new — spawn a terminal worker as a detached headless broker under the root
|
|
103
102
|
// ---------------------------------------------------------------------------
|
|
104
103
|
const nodeNew = defineLeaf({
|
|
105
104
|
name: 'new',
|
|
@@ -108,7 +107,7 @@ const nodeNew = defineLeaf({
|
|
|
108
107
|
tier: 'important',
|
|
109
108
|
help: {
|
|
110
109
|
name: 'node new',
|
|
111
|
-
summary: 'spawn a terminal worker onto the canvas as a
|
|
110
|
+
summary: 'spawn a terminal worker onto the canvas as a detached broker — returns its node id',
|
|
112
111
|
params: [
|
|
113
112
|
{ kind: 'stdin', name: 'prompt', required: true, constraint: 'First user message for the spawned node. Deliver it on stdin from a quoted heredoc (`<<\'EOF\'`) or a file (`< prompt.md`).' },
|
|
114
113
|
{ kind: 'flag', name: 'kind', type: 'string', required: false, default: 'general', constraint: 'Persona kind — match the work to the kind. The <kinds> list below names every installable kind and when to use each; the <sub-personas> block (when present) names the specialist sub-personas available to YOU, each spawnable by its full kind string (e.g. plan/reviewers/security).' },
|
|
@@ -118,14 +117,13 @@ const nodeNew = defineLeaf({
|
|
|
118
117
|
{ kind: 'flag', name: 'parent', type: 'string', required: false, constraint: 'Parent node id. Defaults to the calling node (CRTR_NODE_ID).' },
|
|
119
118
|
{ kind: 'flag', name: 'root', type: 'bool', required: false, constraint: 'Spawn an INDEPENDENT root instead of a managed child: no parent (top-level on the canvas), NO subscription back to you (you are NOT woken by it), resident lifecycle. It records spawned_by=you for provenance and is brought forefront so it can be driven directly. Use for a node you hand off and do not manage (e.g. a sub-orchestrator a human will discuss with).' },
|
|
120
119
|
{ kind: 'flag', name: 'fork-from', type: 'string', required: false, constraint: 'FORK the new node from an existing pi conversation instead of starting it fresh: pass a node id (forks from that node\'s session), an absolute session `.jsonl` path, or a partial pi session uuid. pi copies that whole history into a NEW session for the child (the source is untouched), then the prompt is delivered as the next message — i.e. the child wakes up as a continuation of that conversation. Use to branch exploratory work off a node that already built up the context you need, instead of re-deriving it. One-shot at birth: the fork resumes its own session thereafter.' },
|
|
121
|
-
{ kind: 'flag', name: 'headless', type: 'bool', required: false, constraint: 'Spawn the node on the HEADLESS broker host (no tmux pane) instead of a tmux window. Overrides the `headless` config default for this spawn; omit to use that default (which itself defaults to a tmux pane).' },
|
|
122
120
|
{ kind: 'flag', name: 'model', type: 'enum', choices: ['ultra', 'strong', 'medium', 'light'], required: false, constraint: 'Override the model the node runs on, by capability TIER: ultra (frontier), strong (opus), medium (sonnet), light (haiku). Omit to use the kind\'s persona default (explore=light, every other kind=strong). The override is durable — it survives revives and polymorphs (promote/demote).' },
|
|
123
121
|
],
|
|
124
122
|
output: [
|
|
125
123
|
{ name: 'node_id', type: 'string', required: true, constraint: 'The new node id.' },
|
|
126
124
|
{ name: 'name', type: 'string', required: true, constraint: 'Display name.' },
|
|
127
|
-
{ name: 'window', type: 'string', required: false, constraint: 'tmux window id of the
|
|
128
|
-
{ name: 'session', type: 'string', required:
|
|
125
|
+
{ name: 'window', type: 'string', required: false, constraint: 'tmux window id of the viewer opened for an in-tmux --root (a `crtr attach` view onto the node\'s broker); absent for a managed child, which opens no viewer on spawn.' },
|
|
126
|
+
{ name: 'session', type: 'string', required: false, constraint: 'The tmux session a --root\'s viewer opened into (your current session); absent for a managed child and for a --root spawned outside tmux.' },
|
|
129
127
|
{ name: 'status', type: 'string', required: true, constraint: 'Always "active" on spawn.' },
|
|
130
128
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Decision road sign for the caller: the child runs independently and its finish wakes you on its own, so never wait or poll on it — either pick up other work now or end your turn. If you are an orchestrator already deep in context (>100k), it instead steers you to `crtr node yield` now so your fresh revive absorbs the child\'s result. Read it, then act.' },
|
|
131
129
|
],
|
|
@@ -134,7 +132,7 @@ const nodeNew = defineLeaf({
|
|
|
134
132
|
effects: [
|
|
135
133
|
'Creates a node under ~/.crouter/canvas/nodes/<id>/ and indexes it in canvas.db.',
|
|
136
134
|
'Default (managed child): parent auto-subscribes (active) and is woken on the child\'s pushes. With --root: no subscription — records a spawned_by edge for provenance only.',
|
|
137
|
-
'
|
|
135
|
+
'Launches the node\'s engine as a detached broker (the only host). A managed child opens NO viewer — it runs headless and you open one on demand via `crtr node focus` / `crtr attach`. With --root inside tmux, opens one viewer window in your CURRENT session and switches the client to it so the root can be driven directly; outside tmux a --root opens no viewer.',
|
|
138
136
|
],
|
|
139
137
|
},
|
|
140
138
|
run: async (input) => {
|
|
@@ -150,17 +148,15 @@ const nodeNew = defineLeaf({
|
|
|
150
148
|
const root = input['root'] === true;
|
|
151
149
|
const forkFrom = input['forkFrom'];
|
|
152
150
|
const model = input['model'];
|
|
153
|
-
|
|
154
|
-
const hostKind = input['headless'] === true ? 'broker' : readConfig('user').headless === true ? 'broker' : 'tmux';
|
|
155
|
-
const res = spawnChild({ kind, mode, cwd, name, prompt, parent, root, forkFrom, hostKind, model });
|
|
151
|
+
const res = spawnChild({ kind, mode, cwd, name, prompt, parent, root, forkFrom, model });
|
|
156
152
|
return {
|
|
157
153
|
node_id: res.node.node_id,
|
|
158
154
|
name: res.node.name,
|
|
159
155
|
window: res.window ?? undefined,
|
|
160
|
-
session: res.session,
|
|
156
|
+
session: res.session ?? undefined,
|
|
161
157
|
status: res.node.status,
|
|
162
158
|
follow_up: root
|
|
163
|
-
? "Independent root spawned — it is NOT under you. You are not subscribed, so its finish will NOT wake you and it does not hold you alive; it carries spawned_by=you for lineage only.
|
|
159
|
+
? "Independent root spawned — it is NOT under you. You are not subscribed, so its finish will NOT wake you and it does not hold you alive; it carries spawned_by=you for lineage only. Inside tmux it opened a viewer in your current session (the client switched to it) so it can be driven directly. Hand it off and move on — you will not be notified of its progress."
|
|
164
160
|
: childFollowUp(parent ?? process.env['CRTR_NODE_ID']),
|
|
165
161
|
};
|
|
166
162
|
},
|
|
@@ -184,7 +180,7 @@ const nodeList = defineLeaf({
|
|
|
184
180
|
name: 'node inspect list',
|
|
185
181
|
summary: 'list nodes on the canvas, optionally by status',
|
|
186
182
|
params: [
|
|
187
|
-
{ kind: 'flag', name: 'status', type: 'string', required: false, constraint: 'Filter: active | idle | done | dead | canceled. Comma-separated for several.' },
|
|
183
|
+
{ kind: 'flag', name: 'status', type: 'string', required: false, constraint: 'Filter: active | idle | done | dead | canceled. Comma-separated for several. NOTE: `active` means the engine process is live (never closed), NOT that the node is generating right now — an active node is usually dormant between turns. To tell working-vs-idle, check the pi session-file mtime or CPU, not status.' },
|
|
188
184
|
],
|
|
189
185
|
output: [
|
|
190
186
|
{ name: 'nodes', type: 'object[]', required: true, constraint: 'Rows: {node_id, name, kind, mode, lifecycle, status, cwd, parent, created}.' },
|
|
@@ -243,28 +239,28 @@ const nodeInspect = defineBranch({
|
|
|
243
239
|
children: [nodeList, nodeShow],
|
|
244
240
|
});
|
|
245
241
|
// ---------------------------------------------------------------------------
|
|
246
|
-
// node focus — bring a node's
|
|
242
|
+
// node focus — bring a node's viewer forefront (across roots if needed)
|
|
247
243
|
// ---------------------------------------------------------------------------
|
|
248
244
|
const nodeFocus = defineLeaf({
|
|
249
245
|
name: 'focus',
|
|
250
|
-
description: 'bring a node
|
|
251
|
-
whenToUse: 'you want to bring a specific node into view —
|
|
246
|
+
description: 'bring a node\'s viewer forefront',
|
|
247
|
+
whenToUse: 'you want to bring a specific node into view — open or move its viewer beside your current pane — to watch or steer it directly, reviving its broker first if dormant. Use `node cycle` instead to walk neighbors one viewer at a time rather than jump to a named node, and `node msg` to steer a node without leaving where you are',
|
|
252
248
|
help: {
|
|
253
249
|
name: 'node focus',
|
|
254
|
-
summary: 'bring a node
|
|
250
|
+
summary: 'bring a node\'s viewer forefront — open a `crtr attach` view onto the node\'s broker beside your current pane, or navigate to its existing viewer. The engine runs on the broker (the only host); the pane is just a window onto it.',
|
|
255
251
|
params: [
|
|
256
252
|
{ kind: 'positional', name: 'node', required: true, constraint: 'Node id to focus.' },
|
|
257
|
-
{ kind: 'flag', name: 'new-pane', type: 'bool', required: false, constraint: '
|
|
258
|
-
{ kind: 'flag', name: 'pane', type: 'string', required: false, constraint: 'tmux pane id to focus
|
|
253
|
+
{ kind: 'flag', name: 'new-pane', type: 'bool', required: false, constraint: 'Always open a NEW viewer SIDE-BY-SIDE with your current pane instead of moving/reusing the node\'s existing viewer. Two agents on screen at once (F4).' },
|
|
254
|
+
{ kind: 'flag', name: 'pane', type: 'string', required: false, constraint: 'tmux pane id to focus beside (default: caller TMUX_PANE). Used by the canvas browser popup to focus back into the originating pane.' },
|
|
259
255
|
],
|
|
260
256
|
output: [
|
|
261
|
-
{ name: 'focused', type: 'boolean', required: true, constraint: 'True when the node was brought into view.' },
|
|
262
|
-
{ name: 'session', type: 'string', required: false, constraint: 'The tmux session the
|
|
263
|
-
{ name: 'revived', type: 'boolean', required: true, constraint: 'True when a dormant node was
|
|
264
|
-
{ name: 'in_place', type: 'boolean', required: true, constraint: 'True when
|
|
257
|
+
{ name: 'focused', type: 'boolean', required: true, constraint: 'True when the node\'s viewer was brought into view.' },
|
|
258
|
+
{ name: 'session', type: 'string', required: false, constraint: 'The tmux session the viewer lives in.' },
|
|
259
|
+
{ name: 'revived', type: 'boolean', required: true, constraint: 'True when a dormant node\'s broker was relaunched to be focused.' },
|
|
260
|
+
{ name: 'in_place', type: 'boolean', required: true, constraint: 'True when an existing viewer was navigated to (no new pane); false when a fresh viewer pane was opened or moved beside you.' },
|
|
265
261
|
],
|
|
266
262
|
outputKind: 'object',
|
|
267
|
-
effects: ['
|
|
263
|
+
effects: ['Ensures the node\'s broker engine is alive (revives it if dormant) and opens/moves its one viewer pane (a `crtr attach` view) beside the caller, or navigates to it if a live viewer already sits in the caller\'s session.', 'With --new-pane: always splits a fresh viewer beside the caller (two agents on screen) instead of reusing the existing viewer.'],
|
|
268
264
|
},
|
|
269
265
|
run: async (input) => {
|
|
270
266
|
const id = input['node'];
|
|
@@ -282,14 +278,13 @@ const nodeFocus = defineLeaf({
|
|
|
282
278
|
next: `The pending question is already on the human's screen; see it with \`crtr human list\` / \`crtr human inbox\`, or retract it with \`crtr human cancel ${id}\`.`,
|
|
283
279
|
});
|
|
284
280
|
}
|
|
285
|
-
// Placement owns the whole act
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
// placement need not import revive.ts.
|
|
281
|
+
// Placement owns the whole act: ensure the target's broker engine is alive
|
|
282
|
+
// (revive it if dormant), then open/move its one viewer pane beside the
|
|
283
|
+
// caller (or split a fresh viewer with --new-pane). The reviver is injected
|
|
284
|
+
// so placement need not import revive.ts.
|
|
289
285
|
const res = placementFocus(id, {
|
|
290
286
|
pane: input['pane'],
|
|
291
287
|
newPane: input['newPane'] === true,
|
|
292
|
-
callerNode: process.env['CRTR_NODE_ID'],
|
|
293
288
|
revive: (nid) => { reviveNode(nid, { resume: true }); },
|
|
294
289
|
});
|
|
295
290
|
return { focused: res.focused, session: res.session, revived: res.revived, in_place: res.inPlace };
|
|
@@ -314,7 +309,7 @@ function nodeByWindow(win) {
|
|
|
314
309
|
/** The live node "in front of you" in a tmux pane, HOST-AGNOSTIC. Defaults to
|
|
315
310
|
* $TMUX_PANE / the caller's current pane when `pane` is omitted — shared by
|
|
316
311
|
* `node recycle` / `node demote` / `node lifecycle` / `node close` / `node
|
|
317
|
-
* cycle`, and the `canvas chord`
|
|
312
|
+
* cycle`, and the `canvas chord` leaf.
|
|
318
313
|
*
|
|
319
314
|
* Two resolutions, tried in order:
|
|
320
315
|
* 1. VIEWER pane (broker host): a `crtr attach` viewer self-tags its pane with
|
|
@@ -483,11 +478,11 @@ function liveDfsOrder() {
|
|
|
483
478
|
}
|
|
484
479
|
const nodeCycle = defineLeaf({
|
|
485
480
|
name: 'cycle',
|
|
486
|
-
description: 'DFS-walk to the next/prev live node
|
|
487
|
-
whenToUse: 'sweeping the canvas one
|
|
481
|
+
description: 'DFS-walk to the next/prev live node\'s viewer',
|
|
482
|
+
whenToUse: 'sweeping the canvas one viewer at a time, descending into children before siblings (bound to Alt+] forward / Alt+[ back). Use `node focus` instead to jump straight to a named node',
|
|
488
483
|
help: {
|
|
489
484
|
name: 'node cycle',
|
|
490
|
-
summary: 'focus the next/previous live node in DFS pre-order — the canvas walked one
|
|
485
|
+
summary: 'focus the next/previous live node\'s viewer in DFS pre-order — the canvas walked one viewer at a time, descending into a node\'s children before its siblings (bound to Alt+] forward / Alt+[ back)',
|
|
491
486
|
params: [
|
|
492
487
|
{ kind: 'flag', name: 'dir', type: 'enum', choices: ['next', 'prev'], required: false, default: 'next', constraint: 'Direction along the pre-order: next (Alt+], rightward/deeper into children) or prev (Alt+[, back). Wraps at the ends.' },
|
|
493
488
|
{ kind: 'flag', name: 'pane', type: 'string', required: false, constraint: 'tmux pane to cycle FROM. Defaults to $TMUX_PANE / your current pane. The Alt+] / Alt+[ bindings pass this for you.' },
|
|
@@ -499,7 +494,7 @@ const nodeCycle = defineLeaf({
|
|
|
499
494
|
{ name: 'from', type: 'string', required: false, constraint: 'The node you cycled away from.' },
|
|
500
495
|
],
|
|
501
496
|
outputKind: 'object',
|
|
502
|
-
effects: ['
|
|
497
|
+
effects: ['Brings the neighbor\'s viewer forefront beside the caller (like `node focus`); the viewer you were watching drops to the background.', 'Relaunches the neighbor\'s broker first if it was dormant.'],
|
|
503
498
|
},
|
|
504
499
|
run: async (input) => {
|
|
505
500
|
const pane = input['pane'] ?? process.env['TMUX_PANE'] ?? currentTmux()?.pane ?? undefined;
|
|
@@ -516,12 +511,10 @@ const nodeCycle = defineLeaf({
|
|
|
516
511
|
const target = getNode(targetId);
|
|
517
512
|
if (target === null)
|
|
518
513
|
return { focused: false, from: fromId };
|
|
519
|
-
// Placement
|
|
520
|
-
//
|
|
521
|
-
// is the node we cycled AWAY from — the current occupant of the caller pane.
|
|
514
|
+
// Placement brings the neighbor's viewer forefront beside the caller,
|
|
515
|
+
// relaunching its broker engine first if it was dormant.
|
|
522
516
|
const res = placementFocus(targetId, {
|
|
523
517
|
pane,
|
|
524
|
-
callerNode: fromId,
|
|
525
518
|
revive: (nid) => { reviveNode(nid, { resume: true }); },
|
|
526
519
|
});
|
|
527
520
|
return { focused: res.focused, node_id: targetId, name: target.name, from: fromId };
|
|
@@ -666,12 +659,14 @@ const nodePromote = defineLeaf({
|
|
|
666
659
|
params: [
|
|
667
660
|
{ kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Specialize as this kind of orchestrator. The <kinds> list below names every installable kind and when to use each; the <sub-personas> block (when present) names the specialist sub-personas available to YOU, spawnable by full kind string. Defaults to your current kind. Promoting from a generic kind? CHOOSE a concrete one — it sets the orchestrator persona you revive into.' },
|
|
668
661
|
{ kind: 'flag', name: 'resident', type: 'bool', required: false, constraint: 'ALSO flip lifecycle→resident: make the node interactable — it stays dormant, woken by inbox/human, and is never forced to submit a final. Omit to stay terminal/orchestrator (delegates + holds a roadmap, but still owes a final up the spine and reaps when done).' },
|
|
662
|
+
{ kind: 'flag', name: 'model', type: 'enum', choices: ['ultra', 'strong'], required: false, constraint: 'Change the model you run on, by capability tier: `ultra` (frontier — reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus — regular dev work). Omit to keep your current model. An orchestrator steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives.' },
|
|
669
663
|
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node to promote. Defaults to the caller (CRTR_NODE_ID).' },
|
|
670
664
|
],
|
|
671
665
|
output: [
|
|
672
666
|
{ name: 'node_id', type: 'string', required: true, constraint: 'The promoted node.' },
|
|
673
667
|
{ name: 'kind', type: 'string', required: true, constraint: 'The kind it now orchestrates as.' },
|
|
674
668
|
{ name: 'mode', type: 'string', required: true, constraint: 'Now "orchestrator".' },
|
|
669
|
+
{ name: 'model', type: 'string', required: false, constraint: 'The model tier it now runs on, present only when you changed it with --model.' },
|
|
675
670
|
{ name: 'lifecycle', type: 'string', required: true, constraint: '"resident" if you passed --resident, else unchanged (typically "terminal").' },
|
|
676
671
|
{ name: 'roadmap_written', type: 'boolean', required: true, constraint: 'True if a roadmap scaffold was seeded by this call.' },
|
|
677
672
|
{ name: 'roadmap_path', type: 'string', required: true, constraint: 'Absolute path to your roadmap doc (context/roadmap.md) — edit it to author your plan.' },
|
|
@@ -679,7 +674,7 @@ const nodePromote = defineLeaf({
|
|
|
679
674
|
],
|
|
680
675
|
dynamicState: () => kindsStateBlock(),
|
|
681
676
|
outputKind: 'object',
|
|
682
|
-
effects: ['Flips mode→orchestrator + kind→chosen (lifecycle unchanged unless --resident, which also flips lifecycle→resident); rewrites the launch spec to that kind\'s orchestrator persona; seeds context/roadmap.md scaffold if absent.', 'Your new-role guidance is injected automatically at the turn boundary by the persona injector — the command no longer returns it.'],
|
|
677
|
+
effects: ['Flips mode→orchestrator + kind→chosen (lifecycle unchanged unless --resident, which also flips lifecycle→resident); --model durably pins the model tier; rewrites the launch spec to that kind\'s orchestrator persona; seeds context/roadmap.md scaffold if absent.', 'Your new-role guidance is injected automatically at the turn boundary by the persona injector — the command no longer returns it.'],
|
|
683
678
|
},
|
|
684
679
|
run: async (input) => {
|
|
685
680
|
const id = input['node'] ?? process.env['CRTR_NODE_ID'];
|
|
@@ -688,9 +683,10 @@ const nodePromote = defineLeaf({
|
|
|
688
683
|
const kind = input['kind'];
|
|
689
684
|
if (kind !== undefined)
|
|
690
685
|
assertKind(kind);
|
|
686
|
+
const model = input['model'];
|
|
691
687
|
const resident = input['resident'] === true;
|
|
692
|
-
const res = promote(id, { ...(kind !== undefined ? { kind } : {}), ...(resident ? { resident: true } : {}) });
|
|
693
|
-
return { node_id: res.meta.node_id, kind: res.meta.kind, mode: res.meta.mode, lifecycle: res.meta.lifecycle, roadmap_written: res.roadmapWritten, roadmap_path: res.roadmapPath, goal_path: res.goalPath };
|
|
688
|
+
const res = promote(id, { ...(kind !== undefined ? { kind } : {}), ...(resident ? { resident: true } : {}), ...(model !== undefined ? { model } : {}) });
|
|
689
|
+
return { node_id: res.meta.node_id, kind: res.meta.kind, mode: res.meta.mode, lifecycle: res.meta.lifecycle, ...(res.meta.model_override != null ? { model: res.meta.model_override } : {}), roadmap_written: res.roadmapWritten, roadmap_path: res.roadmapPath, goal_path: res.goalPath };
|
|
694
690
|
},
|
|
695
691
|
});
|
|
696
692
|
// ---------------------------------------------------------------------------
|
|
@@ -713,8 +709,8 @@ function resolveLifecycleNode(input, pane) {
|
|
|
713
709
|
throw new InputError({ error: 'not_found', message: `no node: ${id}`, next: 'List nodes with `crtr node inspect list`.' });
|
|
714
710
|
return id;
|
|
715
711
|
}
|
|
716
|
-
/** Set a node's lifecycle axis and, with `detach`,
|
|
717
|
-
* pane
|
|
712
|
+
/** Set a node's lifecycle axis and, with `detach`, close its foreground viewer
|
|
713
|
+
* pane (the broker engine keeps running). Rebuilds the launch spec so a future
|
|
718
714
|
* revive comes back with the new lifecycle's prompt baked in (the live session
|
|
719
715
|
* is steered by the persona injector; this fixes the static prompt the daemon
|
|
720
716
|
* replays). Spine is fixed by parent-ness, so it carries through unchanged.
|
|
@@ -728,9 +724,9 @@ function setLifecycle(id, value, opts) {
|
|
|
728
724
|
model: target.model_override ?? undefined,
|
|
729
725
|
});
|
|
730
726
|
const meta = updateNode(id, { lifecycle: value, launch });
|
|
731
|
-
// --detach:
|
|
732
|
-
//
|
|
733
|
-
//
|
|
727
|
+
// --detach: stop foregrounding the agent by closing its viewer pane (the
|
|
728
|
+
// broker engine keeps generating off-screen); now terminal, it pushes a final
|
|
729
|
+
// up the spine when it finishes, and a later `focus` reattaches a viewer.
|
|
734
730
|
let detached = false;
|
|
735
731
|
if (opts.detach === true)
|
|
736
732
|
detached = detachToBackground(id, opts.pane);
|
|
@@ -745,23 +741,23 @@ function setLifecycle(id, value, opts) {
|
|
|
745
741
|
// ---------------------------------------------------------------------------
|
|
746
742
|
const nodeDemote = defineLeaf({
|
|
747
743
|
name: 'demote',
|
|
748
|
-
description: 'demote a node to terminal in place; it stays focused and running but now owes a final; --detach also
|
|
749
|
-
whenToUse: 'you are watching a resident/interactive node and want to put it on a finishing track WITHOUT disturbing it: flip it terminal IN PLACE — it keeps its
|
|
744
|
+
description: 'demote a node to terminal in place; it stays focused and running but now owes a final; --detach also closes its viewer (broker keeps running off-screen)',
|
|
745
|
+
whenToUse: 'you are watching a resident/interactive node and want to put it on a finishing track WITHOUT disturbing it: flip it terminal IN PLACE — it keeps its viewer and your focus, keeps running, but now owes a final report up the spine and reaps when done. The friendly counterpart to `node promote`. You can also let go entirely and close its viewer so the agent finishes off-screen on its broker. Use `node recycle` instead to FINISH it now and reboot a fresh root in its pane, and `node lifecycle` for the orthogonal low-level flip (incl. terminal→resident)',
|
|
750
746
|
help: {
|
|
751
747
|
name: 'node demote',
|
|
752
|
-
summary: 'demote a node to terminal IN PLACE — it stays focused and running but now owes a final up the spine and reaps when done. Pairs with `node promote` (mode↑); `--detach` also
|
|
748
|
+
summary: 'demote a node to terminal IN PLACE — it stays focused and running but now owes a final up the spine and reaps when done. Pairs with `node promote` (mode↑); `--detach` also closes its viewer (the broker engine keeps running off-screen)',
|
|
753
749
|
params: [
|
|
754
750
|
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node to demote. Defaults to the node in --pane, else the caller (CRTR_NODE_ID).' },
|
|
755
751
|
{ kind: 'flag', name: 'pane', type: 'string', required: false, constraint: 'tmux pane id whose node to demote, when --node is omitted. Defaults to $TMUX_PANE. The Alt+C menu passes this for you.' },
|
|
756
|
-
{ kind: 'flag', name: 'detach', type: 'bool', required: false, constraint: 'After flipping terminal,
|
|
752
|
+
{ kind: 'flag', name: 'detach', type: 'bool', required: false, constraint: 'After flipping terminal, stop foregrounding the agent by closing its viewer pane. The broker engine keeps generating off-screen and — now terminal — pushes a final up the spine when done; a later `focus` reattaches a viewer. The Alt+C → D move.' },
|
|
757
753
|
],
|
|
758
754
|
output: [
|
|
759
755
|
{ name: 'node_id', type: 'string', required: true, constraint: 'The demoted node.' },
|
|
760
756
|
{ name: 'lifecycle', type: 'string', required: true, constraint: 'Always "terminal" after a demote.' },
|
|
761
|
-
{ name: 'detached', type: 'boolean', required: false, constraint: 'True when --detach
|
|
757
|
+
{ name: 'detached', type: 'boolean', required: false, constraint: 'True when --detach closed the agent\'s viewer pane (broker keeps running).' },
|
|
762
758
|
],
|
|
763
759
|
outputKind: 'object',
|
|
764
|
-
effects: ['Flips the node\'s lifecycle→terminal and rebuilds its launch spec so a future revive boots terminal — it stays focused and running, now owing a final up the spine.', 'The persona injector delivers the transition guidance at the next turn boundary (or on the node\'s next revive if it is dormant).', 'With --detach:
|
|
760
|
+
effects: ['Flips the node\'s lifecycle→terminal and rebuilds its launch spec so a future revive boots terminal — it stays focused and running, now owing a final up the spine.', 'The persona injector delivers the transition guidance at the next turn boundary (or on the node\'s next revive if it is dormant).', 'With --detach: closes the agent\'s viewer pane WITHOUT touching the broker engine — it keeps generating off-screen, reconnectable by a later `focus`.'],
|
|
765
761
|
},
|
|
766
762
|
run: async (input) => {
|
|
767
763
|
const pane = input['pane'] ?? process.env['TMUX_PANE'];
|
|
@@ -769,7 +765,7 @@ const nodeDemote = defineLeaf({
|
|
|
769
765
|
const res = setLifecycle(id, 'terminal', { pane, detach: input['detach'] === true });
|
|
770
766
|
return { node_id: res.node_id, lifecycle: res.lifecycle, detached: res.detached };
|
|
771
767
|
},
|
|
772
|
-
render: (r) => `Demoted ${r['node_id']} — lifecycle now ${r['lifecycle']} (in place)${r['detached'] === true ? ',
|
|
768
|
+
render: (r) => `Demoted ${r['node_id']} — lifecycle now ${r['lifecycle']} (in place)${r['detached'] === true ? ', viewer closed (broker still running off-screen)' : ''}.`,
|
|
773
769
|
});
|
|
774
770
|
// ---------------------------------------------------------------------------
|
|
775
771
|
// node lifecycle — flip the lifecycle axis (terminal ↔ resident), independent
|
|
@@ -781,20 +777,20 @@ const nodeLifecycle = defineLeaf({
|
|
|
781
777
|
whenToUse: 'you want to flip a node\'s LIFECYCLE independent of its mode: make a node RESIDENT so it becomes interactable — it stays dormant, wakes on inbox/human, and is never forced to submit a final; or make a node TERMINAL so it owes a final result up the spine and reaps when done. Orthogonal to `node promote`, which changes MODE (base↔orchestrator), not lifecycle. You can also let go of a still-running agent and send it off-screen to finish in the background. For the human-driver flip-to-terminal pair reach for `node demote` (the friendly terminal-only skin over this)',
|
|
782
778
|
help: {
|
|
783
779
|
name: 'node lifecycle',
|
|
784
|
-
summary: 'set a node\'s lifecycle axis — terminal (owes a final up the spine, reaps when done) or resident (interactable, stays dormant, woken by inbox/human, never forced to submit). Orthogonal to mode; promotion does not touch it. `--detach` also
|
|
780
|
+
summary: 'set a node\'s lifecycle axis — terminal (owes a final up the spine, reaps when done) or resident (interactable, stays dormant, woken by inbox/human, never forced to submit). Orthogonal to mode; promotion does not touch it. `--detach` also closes a live agent\'s viewer (broker keeps running off-screen)',
|
|
785
781
|
params: [
|
|
786
782
|
{ kind: 'positional', name: 'lifecycle', required: true, constraint: 'terminal | resident.' },
|
|
787
783
|
{ kind: 'flag', name: 'node', type: 'string', required: false, constraint: 'Node to change. Defaults to the node in --pane, else the caller (CRTR_NODE_ID).' },
|
|
788
784
|
{ kind: 'flag', name: 'pane', type: 'string', required: false, constraint: 'tmux pane id whose node to change, when --node is omitted. Defaults to $TMUX_PANE. The Alt+C menu passes this for you.' },
|
|
789
|
-
{ kind: 'flag', name: 'detach', type: 'bool', required: false, constraint: 'After flipping lifecycle,
|
|
785
|
+
{ kind: 'flag', name: 'detach', type: 'bool', required: false, constraint: 'After flipping lifecycle, stop foregrounding the agent by closing its viewer pane. The broker engine keeps generating off-screen and — now terminal — pushes a final up the spine when done; a later `focus` reattaches a viewer. The human-driver "I am done foregrounding this" move (Alt+C → D).' },
|
|
790
786
|
],
|
|
791
787
|
output: [
|
|
792
788
|
{ name: 'node_id', type: 'string', required: true, constraint: 'The node.' },
|
|
793
789
|
{ name: 'lifecycle', type: 'string', required: true, constraint: 'Its new lifecycle (terminal | resident).' },
|
|
794
|
-
{ name: 'detached', type: 'boolean', required: false, constraint: 'True when --detach
|
|
790
|
+
{ name: 'detached', type: 'boolean', required: false, constraint: 'True when --detach closed the agent\'s viewer pane (broker keeps running).' },
|
|
795
791
|
],
|
|
796
792
|
outputKind: 'object',
|
|
797
|
-
effects: ['Sets lifecycle on the node meta and rebuilds its launch spec so a future revive boots with the new lifecycle\'s prompt baked in.', 'The persona injector delivers the transition guidance at the next turn boundary (or on the node\'s next revive if it is dormant).', 'With --detach:
|
|
793
|
+
effects: ['Sets lifecycle on the node meta and rebuilds its launch spec so a future revive boots with the new lifecycle\'s prompt baked in.', 'The persona injector delivers the transition guidance at the next turn boundary (or on the node\'s next revive if it is dormant).', 'With --detach: closes the agent\'s viewer pane WITHOUT touching the broker engine — it keeps generating off-screen, reconnectable by a later `focus`.'],
|
|
798
794
|
},
|
|
799
795
|
run: async (input) => {
|
|
800
796
|
const value = input['lifecycle']?.trim().toLowerCase();
|
|
@@ -806,7 +802,7 @@ const nodeLifecycle = defineLeaf({
|
|
|
806
802
|
const res = setLifecycle(id, value, { pane, detach: input['detach'] === true });
|
|
807
803
|
return { node_id: res.node_id, lifecycle: res.lifecycle, detached: res.detached };
|
|
808
804
|
},
|
|
809
|
-
render: (r) => `Set ${r['node_id']} lifecycle → ${r['lifecycle']}${r['detached'] === true ? ',
|
|
805
|
+
render: (r) => `Set ${r['node_id']} lifecycle → ${r['lifecycle']}${r['detached'] === true ? ', viewer closed (broker still running off-screen)' : ''}.`,
|
|
810
806
|
});
|
|
811
807
|
// ---------------------------------------------------------------------------
|
|
812
808
|
// node yield — refresh: discard context, revive fresh against the roadmap
|
|
@@ -814,21 +810,23 @@ const nodeLifecycle = defineLeaf({
|
|
|
814
810
|
const nodeYield = defineLeaf({
|
|
815
811
|
name: 'yield',
|
|
816
812
|
description: 'refresh your context against your roadmap',
|
|
817
|
-
whenToUse: 'your context window is filling up but the mandate is unfinished: request a refresh — end your turn and revive fresh against your roadmap, leaving a note to your future self for the moment you wake. A base node auto-promotes to orchestrator first (a yield needs a roadmap to refresh against). Use `node promote` instead when you need to BECOME an orchestrator with no refresh pending',
|
|
813
|
+
whenToUse: 'your context window is filling up but the mandate is unfinished: request a refresh — end your turn and revive fresh against your roadmap, leaving a note to your future self for the moment you wake. A base node auto-promotes to orchestrator first (a yield needs a roadmap to refresh against). You may also respecialize (--kind) or raise your model (--model) as you refresh. Use `node promote` instead when you need to BECOME an orchestrator with no refresh pending',
|
|
818
814
|
help: {
|
|
819
815
|
name: 'node yield',
|
|
820
|
-
summary: 'request a context refresh — you will be respawned fresh against your roadmap on your next stop (a base node auto-promotes to orchestrator first)',
|
|
816
|
+
summary: 'request a context refresh — you will be respawned fresh against your roadmap on your next stop (a base node auto-promotes to orchestrator first); optionally change your kind or model as you refresh',
|
|
821
817
|
params: [
|
|
822
|
-
{ kind: 'flag', name: 'kind', type: 'string', required: false, constraint: '
|
|
818
|
+
{ kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Respecialize as you refresh — change your kind to this (developer, review, spec, design, plan, explore, general); a base node also auto-promotes into an orchestrator of this kind. Defaults to your current kind.' },
|
|
819
|
+
{ kind: 'flag', name: 'model', type: 'enum', choices: ['ultra', 'strong'], required: false, constraint: 'Change the model your fresh revive runs on, by capability tier: `ultra` (frontier — reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus — regular dev work). Omit to keep your current model. A node steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives.' },
|
|
823
820
|
{ kind: 'stdin', name: 'message', required: true, constraint: 'A note to your future self — what to do the moment you wake fresh. Surfaced as <yield-message> in the next revive. Pass as a positional or pipe via heredoc.' },
|
|
824
821
|
],
|
|
825
822
|
output: [
|
|
826
823
|
{ name: 'node_id', type: 'string', required: true, constraint: 'The yielding node.' },
|
|
827
824
|
{ name: 'promoted', type: 'boolean', required: true, constraint: 'True if this yield promoted a base node to orchestrator.' },
|
|
825
|
+
{ name: 'model', type: 'string', required: false, constraint: 'The model tier your fresh revive will run on, present only when you changed it with --model.' },
|
|
828
826
|
{ name: 'will_refresh', type: 'boolean', required: true, constraint: 'Always true: end your turn and you will revive fresh against your roadmap.' },
|
|
829
827
|
],
|
|
830
828
|
outputKind: 'object',
|
|
831
|
-
effects: ['Sets intent=refresh; the stophook shuts the process down on next stop and the daemon revives it fresh.'],
|
|
829
|
+
effects: ['Sets intent=refresh; the stophook shuts the process down on next stop and the daemon revives it fresh.', '--kind and/or --model durably reshape the node (kind respecialization, model-tier pin) before the refresh-revive; a base node also auto-promotes to orchestrator.'],
|
|
832
830
|
},
|
|
833
831
|
run: async (input) => {
|
|
834
832
|
const id = process.env['CRTR_NODE_ID'];
|
|
@@ -837,13 +835,14 @@ const nodeYield = defineLeaf({
|
|
|
837
835
|
const kind = input['kind'];
|
|
838
836
|
if (kind !== undefined)
|
|
839
837
|
assertKind(kind);
|
|
838
|
+
const model = input['model'];
|
|
840
839
|
const message = typeof input['message'] === 'string' ? input['message'].trim() : '';
|
|
841
840
|
if (message === '') {
|
|
842
841
|
throw new InputError({ error: 'empty_message', message: 'a yield message is required (stdin or positional)', next: 'Pass a note to your future self as an argument or pipe it on stdin.' });
|
|
843
842
|
}
|
|
844
843
|
writeYieldMessage(id, message);
|
|
845
|
-
const res = requestYield(id, kind !== undefined ? { kind } : {});
|
|
846
|
-
return { node_id: res.meta.node_id, promoted: res.promoted, will_refresh: res.willRefresh };
|
|
844
|
+
const res = requestYield(id, { ...(kind !== undefined ? { kind } : {}), ...(model !== undefined ? { model } : {}) });
|
|
845
|
+
return { node_id: res.meta.node_id, promoted: res.promoted, ...(res.meta.model_override != null ? { model: res.meta.model_override } : {}), will_refresh: res.willRefresh };
|
|
847
846
|
},
|
|
848
847
|
});
|
|
849
848
|
// ---------------------------------------------------------------------------
|
|
@@ -1349,7 +1348,7 @@ export function registerNode() {
|
|
|
1349
1348
|
summary: 'spawn and navigate canvas nodes',
|
|
1350
1349
|
model: 'Every agent is a node in one directed graph (the canvas); `subscribes_to` is the spine — spawn a child and you auto-subscribe (active) to it, so its finish wakes you.\n\n' +
|
|
1351
1350
|
'WHEN TO DELEGATE: hand any self-contained unit of work to a child instead of doing it inline — it keeps your own context window (your scarce resource) free for steering. Spawn independent units in PARALLEL; a wake with idle workers is wasted. Serialize only true dependencies, and never let two live children edit the same files.\n\n' +
|
|
1352
|
-
'HOW: `crtr node new "<task>" --kind <kind>` returns a node id immediately and runs the worker
|
|
1351
|
+
'HOW: `crtr node new "<task>" --kind <kind>` returns a node id immediately and runs the worker as a detached headless broker (no window opens; open a viewer on demand with `crtr node focus`). Match the kind to the work (see `node new -h`). You are woken when a child finishes — the wake message ALREADY IS the coalesced digest (the watcher drains your inbox to wake you), so don\'t re-run `crtr feed read` to "open" it (it would read empty, the cursor already advanced); instead dereference the report paths in that digest that matter, don\'t act on a one-line label. (`crtr feed read` is for proactively polling before a wake, or inspecting a child\'s inbox via `--node`; `--all` re-reads history with full message bodies.) Integrate, then either delegate the next units or finish.\n\n' +
|
|
1353
1352
|
'FINISH: a worker ends its own work with `crtr push final "<result>"` (writes the canonical result, marks done, closes the window) — stopping without it is not finishing. For a job too big for one context window, `node promote` to an orchestrator (holds a roadmap, delegates phases); when context fills, `node yield` to refresh against that roadmap.',
|
|
1354
1353
|
},
|
|
1355
1354
|
children: [nodeNew, nodeInspect, nodeFocus, nodeCycle, nodeRecycle, nodeClose, nodeMsg, nodeSubscribe, nodeUnsubscribe, nodePromote, nodeDemote, nodeLifecycle, nodeYield, nodeWake],
|
package/dist/commands/pkg.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { defineBranch } from '../core/command.js';
|
|
5
5
|
import { pluginBranch } from './pkg/plugin.js';
|
|
6
6
|
import { marketBranch } from './pkg/market.js';
|
|
7
|
-
import { bridgeBranch } from './pkg/bridge.js';
|
|
8
7
|
export function registerPkg() {
|
|
9
8
|
return defineBranch({
|
|
10
9
|
name: 'pkg',
|
|
@@ -17,6 +16,6 @@ export function registerPkg() {
|
|
|
17
16
|
name: 'pkg',
|
|
18
17
|
summary: 'manage plugins and plugin marketplaces',
|
|
19
18
|
},
|
|
20
|
-
children: [pluginBranch, marketBranch
|
|
19
|
+
children: [pluginBranch, marketBranch],
|
|
21
20
|
});
|
|
22
21
|
}
|
package/dist/commands/revive.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { defineLeaf } from '../core/command.js';
|
|
7
7
|
import { InputError } from '../core/io.js';
|
|
8
8
|
import { reviveNode } from '../core/runtime/revive.js';
|
|
9
|
+
import { waitForBrokerViewSocket } from '../core/runtime/placement.js';
|
|
9
10
|
import { getNode } from '../core/canvas/index.js';
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
11
12
|
// revive node
|
|
@@ -34,14 +35,16 @@ export const reviveLeaf = defineLeaf({
|
|
|
34
35
|
},
|
|
35
36
|
],
|
|
36
37
|
output: [
|
|
37
|
-
{ name: 'window', type: 'string', required: false, constraint: '
|
|
38
|
-
{ name: 'session', type: 'string', required:
|
|
38
|
+
{ name: 'window', type: 'string', required: false, constraint: 'Always null — the revived broker is headless and opens no tmux window. Kept for caller back-compat.' },
|
|
39
|
+
{ name: 'session', type: 'string', required: false, constraint: 'The node\'s last live location session, or null — the headless broker has no tmux session of its own.' },
|
|
39
40
|
{ name: 'resumed', type: 'boolean', required: true, constraint: 'True when pi was told to --session the saved conversation.' },
|
|
41
|
+
{ name: 'ready', type: 'boolean', required: true, constraint: 'True when the revived broker\'s view.sock accepted a connection before return — the node is immediately attachable/drivable.' },
|
|
40
42
|
],
|
|
41
43
|
outputKind: 'object',
|
|
42
44
|
effects: [
|
|
43
|
-
'
|
|
45
|
+
'Launches the node\'s detached headless broker engine (no tmux window).',
|
|
44
46
|
'Updates the node\'s canvas record: status=active, intent=null, window=<new>.',
|
|
47
|
+
'Blocks until the broker\'s view.sock accepts a connection (up to ~30s), so a caller can attach/dial immediately on return.',
|
|
45
48
|
],
|
|
46
49
|
},
|
|
47
50
|
run: async (input) => {
|
|
@@ -57,10 +60,15 @@ export const reviveLeaf = defineLeaf({
|
|
|
57
60
|
});
|
|
58
61
|
}
|
|
59
62
|
const result = reviveNode(nodeId, { resume: !fresh });
|
|
63
|
+
// Revive returns once the broker process is launched, which can be seconds
|
|
64
|
+
// before its view.sock listens. Callers (attach, the web shell's Wake) dial
|
|
65
|
+
// immediately on return, so block here until the socket accepts.
|
|
66
|
+
const ready = waitForBrokerViewSocket(nodeId);
|
|
60
67
|
return {
|
|
61
68
|
window: result.window ?? undefined,
|
|
62
69
|
session: result.session,
|
|
63
70
|
resumed: result.resumed,
|
|
71
|
+
ready,
|
|
64
72
|
};
|
|
65
73
|
},
|
|
66
74
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const sysSyncLeaf: import("../../core/command.js").LeafDef;
|