@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
|
@@ -20,12 +20,28 @@ export { detectColorCaps };
|
|
|
20
20
|
// Fixed chrome heights, shared with app.ts so its viewport math never drifts
|
|
21
21
|
// from what renderFrame actually draws.
|
|
22
22
|
// header = title + tab bar + status line + separator (+ search input when searching)
|
|
23
|
-
// preview = separator + meta line +
|
|
24
|
-
export const
|
|
23
|
+
// preview = separator + meta line + PROMPT_LINES (spawn/match) + REPLY_LINES (last reply)
|
|
24
|
+
export const PROMPT_LINES = 3;
|
|
25
|
+
export const REPLY_LINES = 3;
|
|
26
|
+
export const PREVIEW_BODY = PROMPT_LINES + REPLY_LINES;
|
|
25
27
|
export const PREVIEW_HEIGHT = PREVIEW_BODY + 2;
|
|
28
|
+
// header = title + tab bar + status line + column header + separator (+ search input)
|
|
26
29
|
export function headerHeight(search) {
|
|
27
|
-
return
|
|
30
|
+
return 5 + (search ? 1 : 0);
|
|
28
31
|
}
|
|
32
|
+
// ── Row table layout ──────────────────────────────────────────────────────────
|
|
33
|
+
// A row is three zones: a fixed-width STATUS rail (glyph + word) on the left, the
|
|
34
|
+
// flexible tree-indented NAME in the middle, and a flush-right METADATA cluster
|
|
35
|
+
// (kind/mode · ctx · age · project) whose fixed-width segments align into clean
|
|
36
|
+
// vertical columns down the right edge. Wide terminals widen the name; narrow ones
|
|
37
|
+
// clip the name (and drop the cluster) first. The status WORD — not a lone glyph —
|
|
38
|
+
// is the legible, NO_COLOR-safe state signal.
|
|
39
|
+
const STATUS_W = 10; // glyph + ' ' + longest word ('active'/'cancel') + trailing gap
|
|
40
|
+
const KM_W = 22; // kind/mode column
|
|
41
|
+
const CTX_W = 6; // ctx tokens column
|
|
42
|
+
const AGE_W = 5; // age column
|
|
43
|
+
const PROJ_W = 16; // project (~basename) column, only across All dirs
|
|
44
|
+
const COL_GAP = ' ';
|
|
29
45
|
// ── ANSI ────────────────────────────────────────────────────────────────────
|
|
30
46
|
const ESC = '\x1b[';
|
|
31
47
|
const RESET = `${ESC}0m`;
|
|
@@ -40,6 +56,7 @@ const FG_RED = '31';
|
|
|
40
56
|
const FG_CYAN = '36';
|
|
41
57
|
const FG_GRAY = '90'; // bright-black
|
|
42
58
|
const FG_BRIGHT_YELLOW = '93';
|
|
59
|
+
const FG_BRIGHT_GREEN = '92'; // streaming pulse (brighter than the active-status green)
|
|
43
60
|
const FG_BRIGHT_CYAN = '96'; // query-match highlight (ties to the cyan search accent)
|
|
44
61
|
const STATUS_GLYPH = {
|
|
45
62
|
active: '●',
|
|
@@ -165,8 +182,67 @@ function nameSpans(name, query, style) {
|
|
|
165
182
|
flush();
|
|
166
183
|
return out;
|
|
167
184
|
}
|
|
168
|
-
/**
|
|
169
|
-
|
|
185
|
+
/** Visible (cell) width of a span group — ANSI-free, surrogate-safe. */
|
|
186
|
+
function spansWidth(spans) {
|
|
187
|
+
let n = 0;
|
|
188
|
+
for (const s of spans)
|
|
189
|
+
n += [...s.text].length;
|
|
190
|
+
return n;
|
|
191
|
+
}
|
|
192
|
+
/** The status rail: a status WORD next to its glyph, both in the status hue, padded
|
|
193
|
+
* to STATUS_W so the rail forms a clean left column. `live` (bright green) when the
|
|
194
|
+
* node is genuinely mid-turn; otherwise the lifecycle word. The word is what makes
|
|
195
|
+
* state legible at a glance and survives NO_COLOR (the glyph is the second cue). */
|
|
196
|
+
function statusRail(r) {
|
|
197
|
+
const streaming = r.streaming === true;
|
|
198
|
+
const word = streaming
|
|
199
|
+
? 'live'
|
|
200
|
+
: ({ active: 'active', idle: 'idle', done: 'done', dead: 'dead', canceled: 'cancel' }[r.status] ?? r.status);
|
|
201
|
+
const fg = streaming ? FG_BRIGHT_GREEN : STATUS_COLOR[r.status];
|
|
202
|
+
const glyph = streaming ? '⟳' : (STATUS_GLYPH[r.status] ?? '?');
|
|
203
|
+
const text = `${glyph} ${word}`;
|
|
204
|
+
const pad = Math.max(1, STATUS_W - [...text].length);
|
|
205
|
+
return [
|
|
206
|
+
{ text, style: { fg, bold: streaming } },
|
|
207
|
+
{ text: ' '.repeat(pad) },
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
/** The flush-right metadata cluster as fixed-width segments, so kind/mode · ctx ·
|
|
211
|
+
* age · project align into vertical columns down the right edge. `showCwd` adds the
|
|
212
|
+
* project column (only meaningful across All dirs). */
|
|
213
|
+
function metaCluster(r, now, showCwd) {
|
|
214
|
+
const ctxFg = ctxColorCode(r.ctx_tokens);
|
|
215
|
+
const out = [
|
|
216
|
+
{ text: clipPad(`${r.kind}/${r.mode}`, KM_W), style: { fg: FG_GRAY } },
|
|
217
|
+
{ text: COL_GAP },
|
|
218
|
+
{ text: fmtCtx(r.ctx_tokens).padStart(CTX_W), style: { fg: ctxFg, dim: ctxFg === undefined } },
|
|
219
|
+
{ text: COL_GAP },
|
|
220
|
+
{ text: relAge(r.created, now).padStart(AGE_W), style: { dim: true } },
|
|
221
|
+
];
|
|
222
|
+
if (showCwd) {
|
|
223
|
+
out.push({ text: COL_GAP }, { text: clipPad(`~${baseDir(r.cwd)}`, PROJ_W), style: { fg: FG_GRAY } });
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
/** Left-clip-pad: clip `s` to `w` cells then pad-end with spaces to exactly `w`. */
|
|
228
|
+
function clipPad(s, w) {
|
|
229
|
+
return clip(s, w).padEnd(w);
|
|
230
|
+
}
|
|
231
|
+
/** Compose a row from a flexible LEFT zone and a fixed flush-right RIGHT cluster:
|
|
232
|
+
* pad fills the gap so RIGHT hugs the edge; when the terminal is too narrow the
|
|
233
|
+
* cluster is dropped rather than colliding with the name. The caller has already
|
|
234
|
+
* clipped the name to fit. */
|
|
235
|
+
function assembleRow(left, right, width, color, lineBase, fill) {
|
|
236
|
+
const leftW = spansWidth(left);
|
|
237
|
+
const rightW = spansWidth(right);
|
|
238
|
+
if (right.length === 0 || leftW + rightW + 1 > width) {
|
|
239
|
+
return assemble(left, width, color, lineBase, fill);
|
|
240
|
+
}
|
|
241
|
+
const pad = width - leftW - rightW;
|
|
242
|
+
return assemble([...left, { text: ' '.repeat(pad) }, ...right], width, color, lineBase, fill);
|
|
243
|
+
}
|
|
244
|
+
/** One table row: `<status rail> <indent><collapse> <name> <flags>` ........ `<meta>`.
|
|
245
|
+
* `showCwd` adds the project column (All-dirs view); `now` drives the age. */
|
|
170
246
|
function rowLine(row, tree, width, isCursor, query, caps, showCwd, now) {
|
|
171
247
|
const node = tree.nodes.get(row.id);
|
|
172
248
|
if (node === undefined)
|
|
@@ -174,30 +250,35 @@ function rowLine(row, tree, width, isCursor, query, caps, showCwd, now) {
|
|
|
174
250
|
const r = node.row;
|
|
175
251
|
const indent = ' '.repeat(row.depth);
|
|
176
252
|
const collapse = !row.hasChildren ? ' ' : row.collapsed ? '▸' : '▾';
|
|
177
|
-
const glyph = STATUS_GLYPH[r.status] ?? '?';
|
|
178
253
|
const terminal = r.status === 'done' || r.status === 'dead' || r.status === 'canceled';
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
{ text: ' ' },
|
|
188
|
-
...nameSpans(r.name, query, nameStyle),
|
|
189
|
-
{ text: ` [${r.kind}/${r.mode}]`, style: { fg: FG_GRAY } }, // recedes
|
|
190
|
-
{ text: ' ctx ', style: { dim: true } },
|
|
191
|
-
{ text: ctxStr, style: { fg: ctxFg, dim: ctxFg === undefined } }, // tiered budget cue
|
|
192
|
-
];
|
|
193
|
-
if (age !== '')
|
|
194
|
-
spans.push({ text: ` ${age}`, style: { dim: true } }); // recency cue
|
|
195
|
-
if (showCwd)
|
|
196
|
-
spans.push({ text: ` ~${baseDir(r.cwd)}`, style: { fg: FG_GRAY } }); // project cue (All dirs)
|
|
254
|
+
// Left gutter (fixed slot): an eye when a viewer is ATTACHED — a scannable rail
|
|
255
|
+
// down the left edge of "what's on a screen right now". Blank otherwise.
|
|
256
|
+
const gutter = r.viewed === true
|
|
257
|
+
? { text: '◉ ', style: { fg: FG_BRIGHT_CYAN, bold: true } }
|
|
258
|
+
: { text: ' ' };
|
|
259
|
+
// Inline attention flag that travels with the name (streaming lives in the rail,
|
|
260
|
+
// attached lives in the gutter).
|
|
261
|
+
const flags = [];
|
|
197
262
|
if (r.asks > 0)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
263
|
+
flags.push({ text: ` ⚑${r.asks}`, style: { fg: FG_BRIGHT_YELLOW, bold: true } }); // pending asks
|
|
264
|
+
const status = statusRail(r);
|
|
265
|
+
const right = metaCluster(r, now, showCwd);
|
|
266
|
+
const treeLead = `${indent}${collapse} `;
|
|
267
|
+
// Clip the NAME (only) so gutter + status + tree + name + flags + meta all fit;
|
|
268
|
+
// the cluster is dropped first (assembleRow) when even a 4-col name won't fit it.
|
|
269
|
+
const fixed = spansWidth([gutter]) + spansWidth(status) + [...treeLead].length + spansWidth(flags);
|
|
270
|
+
const rightW = spansWidth(right);
|
|
271
|
+
const nameMax = Math.max(4, width - fixed - rightW - 1);
|
|
272
|
+
const nameStyle = { dim: !isCursor && terminal, bold: isCursor }; // dim terminal names; bold the cursor row
|
|
273
|
+
const left = [
|
|
274
|
+
gutter,
|
|
275
|
+
...status,
|
|
276
|
+
{ text: treeLead, style: { dim: true } },
|
|
277
|
+
...nameSpans(clip(r.name, nameMax), query, nameStyle),
|
|
278
|
+
...flags,
|
|
279
|
+
];
|
|
280
|
+
// Row base: cursor → subtle bg (256) or reverse fallback (also !color); non-matched
|
|
281
|
+
// ancestor → whole-row dim for tree context.
|
|
201
282
|
let lineBase = '';
|
|
202
283
|
let fill = false;
|
|
203
284
|
if (isCursor) {
|
|
@@ -207,7 +288,26 @@ function rowLine(row, tree, width, isCursor, query, caps, showCwd, now) {
|
|
|
207
288
|
else if (!row.matched) {
|
|
208
289
|
lineBase = DIM;
|
|
209
290
|
}
|
|
210
|
-
return
|
|
291
|
+
return assembleRow(left, right, width, caps.color, lineBase, fill);
|
|
292
|
+
}
|
|
293
|
+
/** The column-header row — dim labels aligned to the same zones the rows use, so
|
|
294
|
+
* the metadata columns read as a table. */
|
|
295
|
+
function columnHeaderLine(width, showCwd, caps) {
|
|
296
|
+
const left = [
|
|
297
|
+
{ text: ' ' }, // viewer gutter (no label)
|
|
298
|
+
{ text: 'STATUS'.padEnd(STATUS_W), style: { dim: true, bold: true } },
|
|
299
|
+
{ text: 'NAME', style: { dim: true, bold: true } },
|
|
300
|
+
];
|
|
301
|
+
const right = [
|
|
302
|
+
{ text: 'KIND/MODE'.padEnd(KM_W), style: { dim: true, bold: true } },
|
|
303
|
+
{ text: COL_GAP },
|
|
304
|
+
{ text: 'CTX'.padStart(CTX_W), style: { dim: true, bold: true } },
|
|
305
|
+
{ text: COL_GAP },
|
|
306
|
+
{ text: 'AGE'.padStart(AGE_W), style: { dim: true, bold: true } },
|
|
307
|
+
];
|
|
308
|
+
if (showCwd)
|
|
309
|
+
right.push({ text: COL_GAP }, { text: 'PROJECT'.padEnd(PROJ_W), style: { dim: true, bold: true } });
|
|
310
|
+
return assembleRow(left, right, width, caps.color, '', false);
|
|
211
311
|
}
|
|
212
312
|
/** The status line (always present): active cwd scope · sort mode · committed
|
|
213
313
|
* filter. The committed-filter indicator lives here now (not its own line) so a
|
|
@@ -248,11 +348,13 @@ function snippetLine(ln, width, caps) {
|
|
|
248
348
|
return assemble(spans, width, caps.color, '', false);
|
|
249
349
|
}
|
|
250
350
|
/** The bottom preview panel — exactly PREVIEW_HEIGHT lines: a separator, a meta
|
|
251
|
-
* line (status · kind/mode · project · age · ctx · asks), then
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
351
|
+
* line (status · kind/mode · project · age · ctx · asks · streaming/viewing), then
|
|
352
|
+
* TWO blocks — the spawn prompt / query-match (PROMPT_LINES) and the node's LAST
|
|
353
|
+
* assistant reply (REPLY_LINES, prefixed `↩`). Under a live query the prompt block
|
|
354
|
+
* is WINDOWED to the matching prompt (anywhere in the conversation) with the match
|
|
355
|
+
* highlighted; with no query it shows the spawn prompt from the start. Seeing both
|
|
356
|
+
* ends — what it was asked and where it left off — answers "which one was this?".
|
|
357
|
+
* Always full height so viewport math holds. */
|
|
256
358
|
function previewPanel(r, width, caps, now, query) {
|
|
257
359
|
const out = [`${DIM}${'─'.repeat(width)}${RESET}`];
|
|
258
360
|
if (r === undefined) {
|
|
@@ -266,23 +368,43 @@ function previewPanel(r, width, caps, now, query) {
|
|
|
266
368
|
const metaPieces = [`${r.status} ${r.kind}/${r.mode}`, baseDir(r.cwd), relAge(r.created, now), `ctx ${fmtCtx(r.ctx_tokens)}`];
|
|
267
369
|
if (r.asks > 0)
|
|
268
370
|
metaPieces.push(`⚑${r.asks}`);
|
|
371
|
+
if (r.streaming === true)
|
|
372
|
+
metaPieces.push('⟳ streaming');
|
|
373
|
+
if (r.viewed === true)
|
|
374
|
+
metaPieces.push('◉ viewing');
|
|
269
375
|
const metaText = clip(metaPieces.filter((p) => p !== '').join(' · '), Math.max(0, width - 2));
|
|
270
376
|
out.push(caps.color ? `${glyph} ${DIM}${metaText}${RESET}` : `${glyph} ${metaText}`);
|
|
271
|
-
// With a query, window+highlight the
|
|
272
|
-
//
|
|
377
|
+
// Block 1 — spawn prompt / query-match. With a query, window+highlight the
|
|
378
|
+
// matching prompt from the WHOLE conversation; otherwise the spawn prompt.
|
|
273
379
|
const sourceText = query !== '' ? promptText(r) : (r.goal ?? '');
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
380
|
+
const promptSnippet = previewSnippet(query, sourceText, width, PROMPT_LINES);
|
|
381
|
+
if (promptSnippet.length === 0) {
|
|
276
382
|
out.push(`${DIM}(no spawn prompt)${RESET}`);
|
|
277
|
-
for (let i = 1; i <
|
|
383
|
+
for (let i = 1; i < PROMPT_LINES; i++)
|
|
278
384
|
out.push('');
|
|
279
385
|
}
|
|
280
386
|
else {
|
|
281
|
-
for (let i = 0; i <
|
|
282
|
-
const ln =
|
|
387
|
+
for (let i = 0; i < PROMPT_LINES; i++) {
|
|
388
|
+
const ln = promptSnippet[i];
|
|
283
389
|
out.push(ln === undefined ? '' : snippetLine(ln, width, caps));
|
|
284
390
|
}
|
|
285
391
|
}
|
|
392
|
+
// Block 2 — the node's LAST assistant reply, prefixed `↩` on its first line so
|
|
393
|
+
// the two blocks read apart. Wrapped to width-2 to leave room for the marker.
|
|
394
|
+
const replyMark = caps.color ? `${DIM}↩ ${RESET}` : '↩ ';
|
|
395
|
+
const replySnippet = previewSnippet('', r.lastAssistant ?? '', Math.max(1, width - 2), REPLY_LINES);
|
|
396
|
+
if (replySnippet.length === 0) {
|
|
397
|
+
out.push(`${replyMark}${DIM}(no reply yet)${RESET}`);
|
|
398
|
+
for (let i = 1; i < REPLY_LINES; i++)
|
|
399
|
+
out.push('');
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
for (let i = 0; i < REPLY_LINES; i++) {
|
|
403
|
+
const ln = replySnippet[i];
|
|
404
|
+
const prefix = i === 0 ? replyMark : ' ';
|
|
405
|
+
out.push(ln === undefined ? (i === 0 ? `${replyMark}` : '') : `${prefix}${snippetLine(ln, Math.max(1, width - 2), caps)}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
286
408
|
return out;
|
|
287
409
|
}
|
|
288
410
|
/**
|
|
@@ -315,11 +437,12 @@ export function renderFrame(state, size, caps = { color: false, color256: false
|
|
|
315
437
|
const slash = caps.color ? `${ESC}${FG_CYAN}m/${RESET}` : '/';
|
|
316
438
|
lines.push(`${slash} ${state.query}▎`);
|
|
317
439
|
}
|
|
318
|
-
// separator.
|
|
440
|
+
// column header (table labels) + separator.
|
|
441
|
+
const showCwd = state.cwdScope === null; // project column only matters across dirs
|
|
442
|
+
lines.push(columnHeaderLine(width, showCwd, caps));
|
|
319
443
|
lines.push(`${DIM}${'─'.repeat(width)}${RESET}`);
|
|
320
444
|
// body — windowed visible rows, leaving room for the (optional) preview panel.
|
|
321
445
|
const head = lines.length; // === headerHeight(state.search)
|
|
322
|
-
const showCwd = state.cwdScope === null; // project cue only matters across dirs
|
|
323
446
|
const previewOn = state.preview && state.visible.length > 0;
|
|
324
447
|
const previewH = previewOn ? PREVIEW_HEIGHT : 0;
|
|
325
448
|
const viewport = Math.max(1, rows - head - 1 /* footer */ - previewH);
|
|
@@ -344,11 +467,19 @@ export function renderFrame(state, size, caps = { color: false, color256: false
|
|
|
344
467
|
for (const l of previewPanel(r, width, caps, now, state.query))
|
|
345
468
|
lines.push(l);
|
|
346
469
|
}
|
|
347
|
-
// footer.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
470
|
+
// footer — the y/n close-out confirm takes over when pending; else search / nav.
|
|
471
|
+
if (state.pendingClose !== null && state.pendingClose !== undefined) {
|
|
472
|
+
const node = state.tree.nodes.get(state.pendingClose);
|
|
473
|
+
const who = node !== undefined ? node.row.name : state.pendingClose;
|
|
474
|
+
const warn = `⚠ ${who} is actively streaming — close it (and its subtree) anyway? y / n`;
|
|
475
|
+
lines.push(caps.color ? `${BOLD}${ESC}${FG_BRIGHT_YELLOW}m${clip(warn, width)}${RESET}` : `${REVERSE}${clip(warn, width)}${RESET}`);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
const footer = state.search
|
|
479
|
+
? '⏎ commit Esc cancel ⌫ delete'
|
|
480
|
+
: '↑↓ move →/← tree ⏎ resume x close Tab tabs / search s sort c cwd r residents p preview q quit';
|
|
481
|
+
lines.push(`${DIM}${clip(footer, width)}${RESET}`);
|
|
482
|
+
}
|
|
352
483
|
// Assemble: home, each line cleared to EOL, then clear below.
|
|
353
484
|
const body = lines.map((l) => `${l}${ESC}K`).join('\r\n');
|
|
354
485
|
return `${ESC}H${body}${ESC}J`;
|
|
@@ -22,7 +22,7 @@ import { ensureHome, ensureNodeDirs, nodeMetaPath, nodeDir, nodesRoot, } from '.
|
|
|
22
22
|
const IDENTITY_KEYS = [
|
|
23
23
|
'node_id', 'name', 'description', 'cycles', 'created', 'cwd', 'host_kind', 'kind', 'mode',
|
|
24
24
|
'lifecycle', 'persona_ack', 'parent', 'spawned_by', 'fork_from', 'passive_default',
|
|
25
|
-
'
|
|
25
|
+
'pi_session_id', 'pi_session_file', 'model_override', 'launch',
|
|
26
26
|
];
|
|
27
27
|
/** Project any node object down to its durable-identity subset. */
|
|
28
28
|
function toIdentity(m) {
|
|
@@ -4,10 +4,10 @@ import type { FocusRow } from './types.js';
|
|
|
4
4
|
export declare function openFocusRow(focus_id: string, pane: string | null, session: string | null, node_id: string): void;
|
|
5
5
|
/** Hot-swap a focus's occupant — single-statement UPDATE. Respects
|
|
6
6
|
* UNIQUE(node_id): if `node_id` already occupies ANOTHER focus this throws
|
|
7
|
-
* (correct — vacate-first is
|
|
7
|
+
* (correct — vacate-first is placement.focus()'s job, not this setter's). */
|
|
8
8
|
export declare function setFocusOccupant(focus_id: string, node_id: string): void;
|
|
9
|
-
/** Re-point a
|
|
10
|
-
*
|
|
9
|
+
/** Re-point a viewer row's durable pane + its derived session cache after the
|
|
10
|
+
* pane moves. Single-statement UPDATE. */
|
|
11
11
|
export declare function setFocusPane(focus_id: string, pane: string | null, session: string | null): void;
|
|
12
12
|
/** DELETE a viewport. */
|
|
13
13
|
export declare function closeFocusRow(focus_id: string): void;
|
|
@@ -16,7 +16,7 @@ export declare function getFocusByNode(node_id: string): FocusRow | null;
|
|
|
16
16
|
/** The focus realized by a given pane (`%id`), or null. */
|
|
17
17
|
export declare function getFocusByPane(pane: string): FocusRow | null;
|
|
18
18
|
/** A focus by its stable id, or null. Used by placement to read a row back by id
|
|
19
|
-
*
|
|
19
|
+
* after openFocusRow registers a viewer. */
|
|
20
20
|
export declare function getFocusById(focus_id: string): FocusRow | null;
|
|
21
21
|
/** Every focus row, ordered by id. */
|
|
22
22
|
export declare function listFocuses(): FocusRow[];
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
// setPresence) — it never runs raw focus SQL itself.
|
|
12
12
|
//
|
|
13
13
|
// Each setter is a single atomic statement. UNIQUE(node_id) upholds "a node
|
|
14
|
-
// occupies at most one
|
|
15
|
-
// for an already-
|
|
16
|
-
// orchestration is
|
|
14
|
+
// occupies at most one viewer" (Q5): a second focus row (or an occupant UPDATE)
|
|
15
|
+
// for an already-viewed node throws — that is correct, the vacate-first
|
|
16
|
+
// orchestration is placement.focus()'s job, not these setters'.
|
|
17
17
|
import { openDb } from './db.js';
|
|
18
18
|
function focusFrom(r) {
|
|
19
19
|
return {
|
|
@@ -35,12 +35,12 @@ export function openFocusRow(focus_id, pane, session, node_id) {
|
|
|
35
35
|
}
|
|
36
36
|
/** Hot-swap a focus's occupant — single-statement UPDATE. Respects
|
|
37
37
|
* UNIQUE(node_id): if `node_id` already occupies ANOTHER focus this throws
|
|
38
|
-
* (correct — vacate-first is
|
|
38
|
+
* (correct — vacate-first is placement.focus()'s job, not this setter's). */
|
|
39
39
|
export function setFocusOccupant(focus_id, node_id) {
|
|
40
40
|
openDb().prepare('UPDATE focuses SET node_id = ? WHERE focus_id = ?').run(node_id, focus_id);
|
|
41
41
|
}
|
|
42
|
-
/** Re-point a
|
|
43
|
-
*
|
|
42
|
+
/** Re-point a viewer row's durable pane + its derived session cache after the
|
|
43
|
+
* pane moves. Single-statement UPDATE. */
|
|
44
44
|
export function setFocusPane(focus_id, pane, session) {
|
|
45
45
|
openDb()
|
|
46
46
|
.prepare('UPDATE focuses SET pane = ?, session = ? WHERE focus_id = ?')
|
|
@@ -68,7 +68,7 @@ export function getFocusByPane(pane) {
|
|
|
68
68
|
return r ? focusFrom(r) : null;
|
|
69
69
|
}
|
|
70
70
|
/** A focus by its stable id, or null. Used by placement to read a row back by id
|
|
71
|
-
*
|
|
71
|
+
* after openFocusRow registers a viewer. */
|
|
72
72
|
export function getFocusById(focus_id) {
|
|
73
73
|
const r = openDb()
|
|
74
74
|
.prepare('SELECT * FROM focuses WHERE focus_id = ?')
|
|
@@ -227,13 +227,14 @@ export function shortId(id) {
|
|
|
227
227
|
}
|
|
228
228
|
// ---------------------------------------------------------------------------
|
|
229
229
|
// Attachment — is a human currently WATCHING a node? A separate axis from
|
|
230
|
-
// running (status 'active' = the engine is live on its
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
// broker
|
|
235
|
-
//
|
|
236
|
-
//
|
|
230
|
+
// running (status 'active' = the engine is live on its detached broker, watched
|
|
231
|
+
// or not). Two signals:
|
|
232
|
+
// viewer pane — a `focuses` row points at the node (one cheap sqlite read per
|
|
233
|
+
// render pass; pane-existence alone is NOT the signal).
|
|
234
|
+
// broker — the broker persists its helloed-viewer count (tmux panes AND
|
|
235
|
+
// web tabs) to job/attach.json on every viewer change
|
|
236
|
+
// (src/core/runtime/broker.ts). Trusted only while the node is
|
|
237
|
+
// 'active': a broker crash can leave a stale file.
|
|
237
238
|
// ---------------------------------------------------------------------------
|
|
238
239
|
/** Node ids currently shown in a tmux focus viewport. Built once per render. */
|
|
239
240
|
export function focusedNodeIds() {
|
|
@@ -32,18 +32,77 @@ export interface DashboardRow {
|
|
|
32
32
|
* agent you come back to. Drives the browser's resident-only lifecycle filter.
|
|
33
33
|
* Only populated by dashboardRowsAll (the browser snapshot). */
|
|
34
34
|
lifecycle?: Lifecycle;
|
|
35
|
-
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
/** "Most recent activity" sort key — epoch ms. The cheap boot stats the node's
|
|
36
|
+
* job/telemetry.json (rewritten on every turn_end at a DETERMINISTIC path, so
|
|
37
|
+
* no meta read), falling back to `created`'s epoch when a node never ran a turn.
|
|
38
|
+
* This is the cheap proxy for last-message recency the attention sort tie-breaks
|
|
39
|
+
* on. Always set by dashboardRowsAll; optional only so synthetic test rows + the
|
|
40
|
+
* scoped dashboardRows can omit it. (NB: the pi session-file mtime would be truer
|
|
41
|
+
* but its path lives in meta.json at a non-deterministic location — reading it
|
|
42
|
+
* per node would defeat the cheap boot, so telemetry mtime is used instead.) */
|
|
43
|
+
mtimeMs?: number;
|
|
44
|
+
/** The node's spawn prompt (context/initial-prompt.md), trimmed + capped. Populated
|
|
45
|
+
* LAZILY by loadPreview (selected-row preview) — NOT on the cheap boot path.
|
|
46
|
+
* Indexed by super-search and shown in the preview panel when there is no query. */
|
|
39
47
|
goal?: string;
|
|
40
|
-
/** EVERY user prompt across the node's pi session — the whole conversation, not
|
|
41
|
-
*
|
|
42
|
-
* for never-revived nodes (no session
|
|
43
|
-
* + the windowed
|
|
48
|
+
/** EVERY user prompt across the node's pi session — the whole conversation, not just
|
|
49
|
+
* the spawn prompt — joined + capped. Populated LAZILY by loadPreview (ONE session
|
|
50
|
+
* read, folded with lastAssistant); undefined for never-revived nodes (no session
|
|
51
|
+
* file). Powers whole-conversation super-search + the windowed preview snippet. */
|
|
44
52
|
prompts?: string;
|
|
53
|
+
/** The node's LAST assistant message (text only), trimmed + capped. Populated LAZILY
|
|
54
|
+
* by loadPreview (same single session read as `prompts`); undefined for a node whose
|
|
55
|
+
* session has no assistant reply yet. Shown in the preview's reply block. */
|
|
56
|
+
lastAssistant?: string;
|
|
57
|
+
/** True when the node is GENUINELY mid-turn right now — its `busy` marker exists
|
|
58
|
+
* AND its broker pid is alive (not merely an `active`, between-turns node). The
|
|
59
|
+
* live "is it generating?" cue. Computed on the cheap boot path for LIVE nodes
|
|
60
|
+
* only (dormant rows are always false). */
|
|
61
|
+
streaming?: boolean;
|
|
62
|
+
/** True when a viewer (focus row) is attached to the node — i.e. someone has it
|
|
63
|
+
* open on screen. Set on the cheap boot path from the one listFocuses() query. */
|
|
64
|
+
viewed?: boolean;
|
|
65
|
+
/** Lazy-enrichment guard (internal): true once enrichRow/enrichRows has folded in
|
|
66
|
+
* the full label (meta), ctx tokens (telemetry), and ⚑ asks. Idempotency marker so
|
|
67
|
+
* progressive paint can re-call enrich for a viewport every keystroke for free. */
|
|
68
|
+
enriched?: boolean;
|
|
69
|
+
/** Lazy-enrichment guard (internal): true once loadPreview has read goal + the
|
|
70
|
+
* session (prompts + lastAssistant). Idempotency marker for the selected row. */
|
|
71
|
+
previewLoaded?: boolean;
|
|
45
72
|
}
|
|
46
73
|
/** One row per node visible in the sub-DAG of `rootId` (including root). */
|
|
47
74
|
export declare function dashboardRows(rootId: string): DashboardRow[];
|
|
48
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* CHEAP boot builder — one row per node across the entire canvas, with only the
|
|
77
|
+
* fields that need no per-node meta/telemetry/session read. Two db queries total:
|
|
78
|
+
* `listNodes()` + `listFocuses()` (the focus set replaces 1473 `getFocusByNode`
|
|
79
|
+
* calls). For each node:
|
|
80
|
+
* - identity/status/kind/mode/cwd/created/lifecycle straight off the db row;
|
|
81
|
+
* - `name` is the db HANDLE only (the informative label needs meta.description —
|
|
82
|
+
* fold it in lazily via {@link enrichRow}/{@link enrichRows});
|
|
83
|
+
* - `viewed` from the one focus-set;
|
|
84
|
+
* - `streaming` only for LIVE nodes (active|idle — the only ones that can be
|
|
85
|
+
* mid-turn); dormant rows are always false (skips an isPidAlive + statSync);
|
|
86
|
+
* - `mtimeMs` via a cheap telemetry statSync (no read) for the attention sort;
|
|
87
|
+
* - `ctx_tokens`/`asks` left 0 and `goal`/`prompts`/`lastAssistant` undefined —
|
|
88
|
+
* all deferred to the lazy enrichment API below.
|
|
89
|
+
*
|
|
90
|
+
* This is the first-frame path: paint from it immediately, then enrich the visible
|
|
91
|
+
* viewport on demand. See {@link enrichRow}, {@link enrichRows}, {@link loadPreview}.
|
|
92
|
+
*/
|
|
49
93
|
export declare function dashboardRowsAll(): DashboardRow[];
|
|
94
|
+
/** Fold the cheap-boot row's deferred fields in for ONE row: the full label
|
|
95
|
+
* (meta.description → fullName), ctx tokens (telemetry.json), and ⚑ asks. One
|
|
96
|
+
* meta read + one telemetry read + one ask scan. Idempotent (no-op once enriched).
|
|
97
|
+
* For a batch, prefer {@link enrichRows} — it scans each cwd's ask inbox once. */
|
|
98
|
+
export declare function enrichRow(row: DashboardRow): DashboardRow;
|
|
99
|
+
/** Batch {@link enrichRow}: enrich every not-yet-enriched row, scanning each
|
|
100
|
+
* distinct cwd's ask inbox exactly ONCE (via `asksForNodes`) instead of per row.
|
|
101
|
+
* Use this for a whole viewport / the full forest; mutates each row in place. */
|
|
102
|
+
export declare function enrichRows(rows: DashboardRow[]): void;
|
|
103
|
+
/** Load the SELECTED row's preview text: the spawn `goal` (initial-prompt.md) plus
|
|
104
|
+
* the whole-conversation `prompts` and the `lastAssistant` reply — the latter two
|
|
105
|
+
* folded into ONE session-file read (see {@link readSessionParts}). Mutates the row
|
|
106
|
+
* in place; idempotent. Call only for the cursor row (and lazily/in-background to
|
|
107
|
+
* warm the prompt super-search corpus), never on the boot path. */
|
|
108
|
+
export declare function loadPreview(row: DashboardRow): DashboardRow;
|