@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
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// pickers.ts — native interactive pickers for `crtr attach`, built against the
|
|
2
|
+
// operator-view wire contract (broker read-op `data` frames). Each builder turns
|
|
3
|
+
// a serialized picker payload into the REAL pi `*SelectorComponent`, wired so a
|
|
4
|
+
// selection dispatches the SAME command frame the text-arg slash path sends and
|
|
5
|
+
// `close()` tears the overlay down. InputController owns the overlay lifecycle
|
|
6
|
+
// (showOverlay/dismiss/focus) and the async data fetch; these are pure builders.
|
|
7
|
+
//
|
|
8
|
+
// FOCUS (load-bearing): some selectors put `handleInput` on an INNER list, not
|
|
9
|
+
// the outer Container (UserMessageSelectorComponent → getMessageList(),
|
|
10
|
+
// SettingsSelectorComponent → getSettingsList()). pi-tui drops every keystroke
|
|
11
|
+
// when the focused component has no `handleInput`, so each builder returns BOTH
|
|
12
|
+
// the overlay `component` AND the `focus` target the caller must focus — exactly
|
|
13
|
+
// as pi's own interactive-mode does (`{ component, focus }`).
|
|
14
|
+
//
|
|
15
|
+
// R1 (contract): the tree/fork/settings payloads are PURE DATA — their selectors
|
|
16
|
+
// build verbatim, no reconstruction. The MODEL picker's `ModelSelectorComponent`
|
|
17
|
+
// needs a live `ModelRegistry`/`SettingsManager`: we feed the REAL component a
|
|
18
|
+
// minimal in-memory `SettingsManager` plus a tiny `ModelRegistry` adapter (the
|
|
19
|
+
// component calls only refresh/getError/getAvailable/find — verified against
|
|
20
|
+
// model-selector.js), preserving its full search + scope-toggle UI without
|
|
21
|
+
// crossing engine state over the socket. The SESSION picker reconstructs
|
|
22
|
+
// faithfully from async loaders (no shim). The SCOPED-MODELS component is NOT
|
|
23
|
+
// re-exported by pi (through 0.79.1) AND has no mutation frame in the contract,
|
|
24
|
+
// so it renders read-only via a pi-tui SelectList.
|
|
25
|
+
//
|
|
26
|
+
// pi pin is `^0.79.1` (a RANGE): the duck-typed registry adapter relies on the
|
|
27
|
+
// component's current 4-method registry surface — a future 0.79.x adding a
|
|
28
|
+
// registry call would surface at runtime (caught by live verification).
|
|
29
|
+
import { SelectList, } from '@earendil-works/pi-tui';
|
|
30
|
+
import { getSelectListTheme, ModelSelectorComponent, SessionSelectorComponent, SettingsSelectorComponent, TreeSelectorComponent, UserMessageSelectorComponent, SettingsManager, } from '@earendil-works/pi-coding-agent';
|
|
31
|
+
/** A minimal `ModelRegistry` for `ModelSelectorComponent`: the component calls
|
|
32
|
+
* only `refresh()`, `getError()`, `getAvailable()`, and `find()` (verified
|
|
33
|
+
* against pi `model-selector.js`). We satisfy exactly those from the wire
|
|
34
|
+
* payload — no auth/registry state crosses the socket — and cast to the nominal
|
|
35
|
+
* class the ctor wants. */
|
|
36
|
+
function modelRegistryAdapter(all, available) {
|
|
37
|
+
const adapter = {
|
|
38
|
+
refresh() { },
|
|
39
|
+
getError() {
|
|
40
|
+
return undefined;
|
|
41
|
+
},
|
|
42
|
+
getAvailable() {
|
|
43
|
+
return available;
|
|
44
|
+
},
|
|
45
|
+
find(provider, id) {
|
|
46
|
+
return all.find((m) => m.provider === provider && m.id === id);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
return adapter;
|
|
50
|
+
}
|
|
51
|
+
/** `/model` (and ctrl+l) — the real model selector with search + scope toggle.
|
|
52
|
+
* Select → `set_model` with the resolved `provider/id` (the broker's
|
|
53
|
+
* `findModelSpec` requires that form). */
|
|
54
|
+
export function buildModelPicker(tui, data, send, close) {
|
|
55
|
+
const all = data.models;
|
|
56
|
+
const availableSet = new Set(data.availableIds);
|
|
57
|
+
const available = all.filter((m) => availableSet.has(`${m.provider}/${m.id}`));
|
|
58
|
+
const current = data.current
|
|
59
|
+
? all.find((m) => m.provider === data.current.provider && m.id === data.current.id)
|
|
60
|
+
: undefined;
|
|
61
|
+
const component = new ModelSelectorComponent(tui, current, SettingsManager.inMemory(), modelRegistryAdapter(all, available), data.scopedModels, (model) => {
|
|
62
|
+
send({ type: 'set_model', model: `${model.provider}/${model.id}` });
|
|
63
|
+
close();
|
|
64
|
+
}, () => close());
|
|
65
|
+
return { component, focus: component };
|
|
66
|
+
}
|
|
67
|
+
/** `/resume` — the real session selector. Reconstructs from async loaders that
|
|
68
|
+
* fetch each scope on demand (cwd is pre-fetched so the current-session marker
|
|
69
|
+
* shows immediately; the `all` scope loads only on toggle). Select →
|
|
70
|
+
* `switch_session`. Rename/delete are out of scope (no command frame). */
|
|
71
|
+
export function buildSessionPicker(tui, prefetchedCwd, request, keybindings, send, close) {
|
|
72
|
+
const revive = (d) => d.sessions.map((s) => ({ ...s, created: new Date(s.created), modified: new Date(s.modified) }));
|
|
73
|
+
let firstCwd = true;
|
|
74
|
+
const currentLoader = async () => {
|
|
75
|
+
if (firstCwd) {
|
|
76
|
+
firstCwd = false;
|
|
77
|
+
return revive(prefetchedCwd);
|
|
78
|
+
}
|
|
79
|
+
return revive((await request({ type: 'list_sessions', scope: 'cwd' })));
|
|
80
|
+
};
|
|
81
|
+
const allLoader = async () => revive((await request({ type: 'list_sessions', scope: 'all' })));
|
|
82
|
+
const component = new SessionSelectorComponent(currentLoader, allLoader, (path) => {
|
|
83
|
+
send({ type: 'switch_session', path });
|
|
84
|
+
close();
|
|
85
|
+
}, () => close(), () => close(), () => tui.requestRender(), { showRenameHint: false, keybindings: keybindings }, prefetchedCwd.currentSessionFile);
|
|
86
|
+
return { component, focus: component };
|
|
87
|
+
}
|
|
88
|
+
/** `/tree` — the real tree navigator (pure data). Select → `navigate_tree`.
|
|
89
|
+
* Label-edit is out of scope (no command frame) → no `onLabelChange`. */
|
|
90
|
+
export function buildTreePicker(tui, data, send, close) {
|
|
91
|
+
const terminalHeight = process.stdout.rows ?? 24;
|
|
92
|
+
const component = new TreeSelectorComponent(data.tree, data.currentLeafId, terminalHeight, (entryId) => {
|
|
93
|
+
send({ type: 'navigate_tree', targetId: entryId });
|
|
94
|
+
close();
|
|
95
|
+
}, () => close());
|
|
96
|
+
return { component, focus: component };
|
|
97
|
+
}
|
|
98
|
+
/** `/fork` — the real prior-user-message selector (pure data, from
|
|
99
|
+
* `get_tree.forkPoints`). Select → `fork`. The outer component has no
|
|
100
|
+
* `handleInput`; the inner `getMessageList()` does (focus target). */
|
|
101
|
+
export function buildForkPicker(data, send, close) {
|
|
102
|
+
const component = new UserMessageSelectorComponent(data.forkPoints, (entryId) => {
|
|
103
|
+
send({ type: 'fork', entryId });
|
|
104
|
+
close();
|
|
105
|
+
}, () => close());
|
|
106
|
+
return { component, focus: component.getMessageList() };
|
|
107
|
+
}
|
|
108
|
+
/** `/settings` — the real settings menu (the wire `settings` IS a SettingsConfig
|
|
109
|
+
* superset). Only the contract-enumerated mutations are wired: thinking level →
|
|
110
|
+
* `set_thinking_level`, auto-compact → `set_auto_compaction`. Every other toggle
|
|
111
|
+
* has no command frame (out of scope), so its callback notifies rather than
|
|
112
|
+
* silently lying that the change took. The theme submenu is viewer-local (CTO).
|
|
113
|
+
* The outer component has no `handleInput`; `getSettingsList()` is the focus
|
|
114
|
+
* target. */
|
|
115
|
+
export function buildSettingsPicker(data, send, close, notify) {
|
|
116
|
+
const out = () => notify('Only thinking level + auto-compaction are adjustable over view.sock');
|
|
117
|
+
const callbacks = {
|
|
118
|
+
onThinkingLevelChange: (level) => send({ type: 'set_thinking_level', level }),
|
|
119
|
+
onAutoCompactChange: (enabled) => send({ type: 'set_auto_compaction', enabled }),
|
|
120
|
+
onCancel: () => close(),
|
|
121
|
+
onShowImagesChange: out,
|
|
122
|
+
onImageWidthCellsChange: out,
|
|
123
|
+
onAutoResizeImagesChange: out,
|
|
124
|
+
onBlockImagesChange: out,
|
|
125
|
+
onEnableSkillCommandsChange: out,
|
|
126
|
+
onSteeringModeChange: out,
|
|
127
|
+
onFollowUpModeChange: out,
|
|
128
|
+
onTransportChange: out,
|
|
129
|
+
onHttpIdleTimeoutMsChange: out,
|
|
130
|
+
onThemeChange: out,
|
|
131
|
+
onHideThinkingBlockChange: out,
|
|
132
|
+
onCollapseChangelogChange: out,
|
|
133
|
+
onEnableInstallTelemetryChange: out,
|
|
134
|
+
onDoubleEscapeActionChange: out,
|
|
135
|
+
onTreeFilterModeChange: out,
|
|
136
|
+
onShowHardwareCursorChange: out,
|
|
137
|
+
onEditorPaddingXChange: out,
|
|
138
|
+
onAutocompleteMaxVisibleChange: out,
|
|
139
|
+
onQuietStartupChange: out,
|
|
140
|
+
onDefaultProjectTrustChange: out,
|
|
141
|
+
onClearOnShrinkChange: out,
|
|
142
|
+
onShowTerminalProgressChange: out,
|
|
143
|
+
onWarningsChange: out,
|
|
144
|
+
};
|
|
145
|
+
const component = new SettingsSelectorComponent(data.settings, callbacks);
|
|
146
|
+
return { component, focus: component.getSettingsList() };
|
|
147
|
+
}
|
|
148
|
+
/** `/scoped-models` — READ-ONLY via a pi-tui SelectList. CONTRACT GAP: pi (through
|
|
149
|
+
* 0.79.1) does not re-export `ScopedModelsSelectorComponent`, and the contract
|
|
150
|
+
* exposes no command frame for its enable/disable/persist mutations, so a
|
|
151
|
+
* faithful editable picker is not buildable in scope. This shows the registry
|
|
152
|
+
* with the enabled set marked; select notifies that toggling is unsupported. */
|
|
153
|
+
export function buildScopedModelsPicker(data, close, notify) {
|
|
154
|
+
const enabled = data.enabledModelIds;
|
|
155
|
+
const isEnabled = (m) => {
|
|
156
|
+
if (enabled === null)
|
|
157
|
+
return true; // null = no scoping, every model cycles
|
|
158
|
+
const ref = `${m.provider}/${m.id}`;
|
|
159
|
+
return enabled.includes(ref) || enabled.includes(m.id) || enabled.includes('*');
|
|
160
|
+
};
|
|
161
|
+
const items = data.allModels.map((m) => ({
|
|
162
|
+
value: `${m.provider}/${m.id}`,
|
|
163
|
+
label: `${isEnabled(m) ? '✓' : ' '} ${m.provider}/${m.id}`,
|
|
164
|
+
description: m.name,
|
|
165
|
+
}));
|
|
166
|
+
const theme = getSelectListTheme();
|
|
167
|
+
const list = new SelectList(items, 12, theme);
|
|
168
|
+
list.onSelect = () => notify("Enabling/disabling scoped models isn't supported over view.sock yet");
|
|
169
|
+
list.onCancel = () => close();
|
|
170
|
+
return { component: list, focus: list };
|
|
171
|
+
}
|
|
@@ -21,6 +21,28 @@ export interface SlashContext {
|
|
|
21
21
|
* registers `/graph` as a stub that calls this hook. Unit C/Q owns the overlay
|
|
22
22
|
* itself and populates this; when absent `/graph` notifies it isn't wired yet. */
|
|
23
23
|
onGraph?: () => void;
|
|
24
|
+
/** Detach this viewer (`/quit`). A viewer quit DETACHES — it tears down the
|
|
25
|
+
* local TUI/socket and leaves the shared engine running. Wired by attach-cmd to
|
|
26
|
+
* `teardown('detach')`; without it `/quit` cannot actually exit (just sending
|
|
27
|
+
* `bye` makes the broker drop the socket, which the viewer treats as a dropped
|
|
28
|
+
* connection and reconnects). */
|
|
29
|
+
onQuit?: () => void;
|
|
30
|
+
/** Copy the last assistant message to the clipboard (`/copy`). Wired by
|
|
31
|
+
* attach-cmd (it owns the ChatView that holds the text + the clipboard writer). */
|
|
32
|
+
onCopy?: () => void;
|
|
33
|
+
/** OPTIONAL native-picker openers — the InputController fills these from its
|
|
34
|
+
* read-op request channel. When absent (channel unwired) the value-picker
|
|
35
|
+
* builtins fall back to the text-arg notice. */
|
|
36
|
+
openModelPicker?: () => void;
|
|
37
|
+
openSessionPicker?: () => void;
|
|
38
|
+
openForkPicker?: () => void;
|
|
39
|
+
openTreePicker?: () => void;
|
|
40
|
+
openSettingsPicker?: () => void;
|
|
41
|
+
openScopedModelsPicker?: () => void;
|
|
42
|
+
/** Open the native /login provider selector. */
|
|
43
|
+
openLoginPicker?: () => void;
|
|
44
|
+
/** Open the native /logout provider selector. */
|
|
45
|
+
openLogoutPicker?: () => void;
|
|
24
46
|
}
|
|
25
47
|
/** Canvas slash-commands — reimplemented NATIVE in the viewer (the canvas chrome
|
|
26
48
|
* pi-extensions cannot load here; see findings-chrome-parity.md Feature 3). Each
|
|
@@ -7,23 +7,23 @@
|
|
|
7
7
|
// doesn't recognize) fall through to be sent as a `prompt` for the engine's
|
|
8
8
|
// extension runner to handle.
|
|
9
9
|
//
|
|
10
|
-
// SELECTORS —
|
|
11
|
-
// `/
|
|
12
|
-
//
|
|
13
|
-
// socket
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// notice with the arg form. (This protocol gap is reported up to the parent.)
|
|
10
|
+
// SELECTORS — native interactive pickers. `/model`, `/resume`, `/fork`, `/tree`,
|
|
11
|
+
// `/settings`, `/scoped-models` open pi's real `*SelectorComponent`s as overlays,
|
|
12
|
+
// fed by the operator-view read-op `data` frames (broker-protocol) over the
|
|
13
|
+
// socket. With no arg they open the picker (via the `openXPicker` SlashContext
|
|
14
|
+
// callbacks the InputController exposes, fed by its read-op request channel —
|
|
15
|
+
// which attach-cmd MUST wire via `onRequest: (f) => socket.request(f)`). With an
|
|
16
|
+
// arg the same resolved value is sent directly (`/model <provider/id>`, `/resume
|
|
17
|
+
// <path>`, `/fork <entryId>`, `/tree <entryId>`) without the UI. If the request
|
|
18
|
+
// channel is unwired the picker degrades to the arg-form notice. (Verified
|
|
19
|
+
// against pi 0.79.1 — `ScopedModelsSelectorComponent` is the one selector pi does
|
|
20
|
+
// not re-export, so /scoped-models renders read-only via a SelectList.)
|
|
22
21
|
//
|
|
23
22
|
// SCOPED OUT (review m2 — no engine method): `/login`, `/logout`, `/share`,
|
|
24
23
|
// `/trust` — a brief "not supported in attach" notice, never a frame.
|
|
25
24
|
import { execFile } from 'node:child_process';
|
|
26
25
|
import { BUILTIN_SLASH_COMMANDS } from '../../core/runtime/pi-vendored.js';
|
|
26
|
+
import { surfaceTmuxStyleArgs } from '../../core/runtime/surface-bg.js';
|
|
27
27
|
/** Canvas slash-commands — reimplemented NATIVE in the viewer (the canvas chrome
|
|
28
28
|
* pi-extensions cannot load here; see findings-chrome-parity.md Feature 3). Each
|
|
29
29
|
* ports the action of its extension: `/promote` ← canvas-commands.ts, `/resume-node`
|
|
@@ -39,7 +39,7 @@ export const CANVAS_SLASH_COMMANDS = [
|
|
|
39
39
|
];
|
|
40
40
|
const CANVAS_NAMES = new Set(CANVAS_SLASH_COMMANDS.map((c) => c.name));
|
|
41
41
|
/** Builtins with no Phase-4 engine method — scoped out (review m2). */
|
|
42
|
-
const SCOPED_OUT = new Set(['
|
|
42
|
+
const SCOPED_OUT = new Set(['trust']);
|
|
43
43
|
/** Valid `/settings thinking` levels — the `SetThinkingLevelFrame['level']`
|
|
44
44
|
* union (= AgentSession['thinkingLevel'], pi-agent-core ThinkingLevel),
|
|
45
45
|
* mirrored as a runtime set so an invalid arg is rejected with the options
|
|
@@ -95,6 +95,13 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
95
95
|
return true;
|
|
96
96
|
case 'quit':
|
|
97
97
|
// A viewer "quit" DETACHES — the shared engine keeps running (one-writer).
|
|
98
|
+
// onQuit tears the viewer down locally (which sends `bye` itself). Falling
|
|
99
|
+
// back to a bare `bye` would just make the broker close the socket, which
|
|
100
|
+
// the viewer reconnects to — so the detach MUST go through onQuit.
|
|
101
|
+
if (ctx.onQuit) {
|
|
102
|
+
ctx.onQuit();
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
98
105
|
ctx.send({ type: 'bye' });
|
|
99
106
|
ctx.notify('Detaching — the engine keeps running');
|
|
100
107
|
return true;
|
|
@@ -116,27 +123,34 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
116
123
|
return usage(ctx, '/import <path-to-session.jsonl>');
|
|
117
124
|
ctx.send({ type: 'switch_session', path: arg });
|
|
118
125
|
return true;
|
|
119
|
-
// --- value-picker builtins:
|
|
126
|
+
// --- value-picker builtins: native picker (no arg) OR direct value (arg) ---
|
|
120
127
|
case 'model':
|
|
121
128
|
if (!arg)
|
|
122
|
-
return
|
|
129
|
+
return openOrHint(ctx.openModelPicker, ctx, 'model', '/model <provider/id>');
|
|
123
130
|
ctx.send({ type: 'set_model', model: arg });
|
|
124
131
|
return true;
|
|
125
132
|
case 'resume':
|
|
126
133
|
if (!arg)
|
|
127
|
-
return
|
|
134
|
+
return openOrHint(ctx.openSessionPicker, ctx, 'session', '/resume <session-path>');
|
|
128
135
|
ctx.send({ type: 'switch_session', path: arg });
|
|
129
136
|
return true;
|
|
130
137
|
case 'fork':
|
|
131
138
|
if (!arg)
|
|
132
|
-
return
|
|
139
|
+
return openOrHint(ctx.openForkPicker, ctx, 'fork point', '/fork <entryId>');
|
|
133
140
|
ctx.send({ type: 'fork', entryId: arg });
|
|
134
141
|
return true;
|
|
135
142
|
case 'tree':
|
|
136
143
|
if (!arg)
|
|
137
|
-
return
|
|
144
|
+
return openOrHint(ctx.openTreePicker, ctx, 'tree', '/tree <entryId>');
|
|
138
145
|
ctx.send({ type: 'navigate_tree', targetId: arg });
|
|
139
146
|
return true;
|
|
147
|
+
case 'scoped-models':
|
|
148
|
+
if (ctx.openScopedModelsPicker) {
|
|
149
|
+
ctx.openScopedModelsPicker();
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
ctx.notify('The scoped-models view needs the read-op channel, which this viewer has not wired');
|
|
153
|
+
return true;
|
|
140
154
|
// --- settings (sub-command form; interactive menu needs read ops) ------
|
|
141
155
|
case 'settings':
|
|
142
156
|
return handleSettings(arg, ctx);
|
|
@@ -144,12 +158,44 @@ export function dispatchSlashCommand(text, ctx) {
|
|
|
144
158
|
case 'session':
|
|
145
159
|
ctx.notify(describeSession(ctx.state));
|
|
146
160
|
return true;
|
|
147
|
-
// --- recognized builtins with no Phase-4 wire path --------------------
|
|
148
161
|
case 'copy':
|
|
162
|
+
// Copy the last assistant message to the clipboard — viewer-owned (the text
|
|
163
|
+
// is in the ChatView; no engine round-trip). attach-cmd does the actual copy.
|
|
164
|
+
if (ctx.onCopy) {
|
|
165
|
+
ctx.onCopy();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
ctx.notify('/copy is not available in this viewer');
|
|
169
|
+
return true;
|
|
170
|
+
// --- auth commands — viewer-local OAuth/API-key flows ------------------
|
|
171
|
+
case 'login':
|
|
172
|
+
if (ctx.openLoginPicker) {
|
|
173
|
+
ctx.openLoginPicker();
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
ctx.notify('/login is not available in this viewer');
|
|
177
|
+
return true;
|
|
178
|
+
case 'logout':
|
|
179
|
+
if (ctx.openLogoutPicker) {
|
|
180
|
+
ctx.openLogoutPicker();
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
ctx.notify('/logout is not available in this viewer');
|
|
184
|
+
return true;
|
|
185
|
+
// --- share / clone / changelog -----------------------------------------
|
|
186
|
+
case 'share':
|
|
187
|
+
ctx.send({ type: 'share' });
|
|
188
|
+
ctx.notify('Sharing session…');
|
|
189
|
+
return true;
|
|
190
|
+
case 'clone':
|
|
191
|
+
ctx.send({ type: 'clone' });
|
|
192
|
+
ctx.notify('Cloning session…');
|
|
193
|
+
return true;
|
|
149
194
|
case 'changelog':
|
|
195
|
+
ctx.notify('/changelog: see https://github.com/earendil-works/pi for release notes (no bundled changelog in this build)');
|
|
196
|
+
return true;
|
|
197
|
+
// --- recognized builtins with no Phase-4 wire path --------------------
|
|
150
198
|
case 'hotkeys':
|
|
151
|
-
case 'scoped-models':
|
|
152
|
-
case 'clone':
|
|
153
199
|
ctx.notify(`/${name} is not available in attach (Phase 4)`);
|
|
154
200
|
return true;
|
|
155
201
|
default:
|
|
@@ -295,7 +341,7 @@ function openView(arg, ctx) {
|
|
|
295
341
|
* screen and closes itself when the command exits, dropping back to this pane. */
|
|
296
342
|
function popup(cmd) {
|
|
297
343
|
try {
|
|
298
|
-
execFile('tmux', ['display-popup', '-E', '-w', '90%', '-h', '85%', cmd], () => {
|
|
344
|
+
execFile('tmux', ['display-popup', '-E', '-w', '90%', '-h', '85%', ...surfaceTmuxStyleArgs(), cmd], () => {
|
|
299
345
|
/* best-effort: popup is self-contained */
|
|
300
346
|
});
|
|
301
347
|
}
|
|
@@ -316,6 +362,16 @@ function pickerHint(ctx, what, form) {
|
|
|
316
362
|
ctx.notify(`Interactive ${what} picker isn't available over view.sock yet — use ${form}`);
|
|
317
363
|
return true;
|
|
318
364
|
}
|
|
365
|
+
/** Open a native picker if its opener is wired; otherwise fall back to the
|
|
366
|
+
* text-arg hint. Keeps every value-picker builtin working even when the
|
|
367
|
+
* read-op request channel is absent. */
|
|
368
|
+
function openOrHint(open, ctx, what, form) {
|
|
369
|
+
if (open) {
|
|
370
|
+
open();
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
return pickerHint(ctx, what, form);
|
|
374
|
+
}
|
|
319
375
|
function handleSettings(arg, ctx) {
|
|
320
376
|
const [sub, ...rest] = arg.split(/\s+/);
|
|
321
377
|
const value = rest.join(' ').trim();
|
|
@@ -335,12 +391,22 @@ function handleSettings(arg, ctx) {
|
|
|
335
391
|
case 'auto-compaction':
|
|
336
392
|
ctx.send({ type: 'set_auto_compaction', enabled: parseBool(value) });
|
|
337
393
|
return true;
|
|
394
|
+
case '':
|
|
395
|
+
// Bare `/settings` opens the native settings menu (falls back to the
|
|
396
|
+
// sub-command summary when the picker channel is unwired).
|
|
397
|
+
if (ctx.openSettingsPicker) {
|
|
398
|
+
ctx.openSettingsPicker();
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
return settingsSummary(ctx);
|
|
338
402
|
default:
|
|
339
|
-
ctx
|
|
340
|
-
'(the interactive settings menu needs data not exposed over view.sock in Phase 4)');
|
|
341
|
-
return true;
|
|
403
|
+
return settingsSummary(ctx);
|
|
342
404
|
}
|
|
343
405
|
}
|
|
406
|
+
function settingsSummary(ctx) {
|
|
407
|
+
ctx.notify('Settings: /settings thinking <level> | auto-retry on|off | auto-compaction on|off');
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
344
410
|
function parseBool(value) {
|
|
345
411
|
return /^(on|true|yes|1|enabled?)$/i.test(value.trim());
|
|
346
412
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { CustomEditor } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
/** Thinking levels pi cycles through (shift+tab), lowest → highest budget. */
|
|
3
|
+
export type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
4
|
+
/** The default (thinking `off`) title chip: reverse video + a space of padding
|
|
5
|
+
* each side, so the name reads as a label sitting on the border rule. Used as
|
|
6
|
+
* the fallback when no thinking color applies. */
|
|
7
|
+
export declare const defaultTitleStyle: (s: string) => string;
|
|
8
|
+
/** Resolve the editor border colorizer for a thinking level. Unknown / `off` →
|
|
9
|
+
* the supplied `fallback` (the theme's default border color). */
|
|
10
|
+
export declare function thinkingBorderColor(level: string | undefined, fallback: (s: string) => string): (s: string) => string;
|
|
11
|
+
/** Resolve the title-chip styler for a thinking level: the level's color as the
|
|
12
|
+
* chip BACKGROUND with bold white text (a space of padding each side), so the
|
|
13
|
+
* session name reads as a solid label in the same hue as the border. Unknown /
|
|
14
|
+
* `off` → the supplied `fallback` (the reverse-video default chip). */
|
|
15
|
+
export declare function thinkingTitleStyle(level: string | undefined, fallback: (s: string) => string): (s: string) => string;
|
|
16
|
+
/** Compose the replacement top-border line: solid title chip + border rule +
|
|
17
|
+
* info chip, never wider than `width` (pi-tui hard-crashes on an over-wide
|
|
18
|
+
* line). The info chip yields entirely when the chip leaves it almost no room,
|
|
19
|
+
* and truncates when it only partially fits. Exported pure for the overflow
|
|
20
|
+
* regression test. */
|
|
21
|
+
export declare function composeTopBorder(width: number, title: string, info: string, titleStyle: (s: string) => string, borderColor: (s: string) => string): string;
|
|
22
|
+
export declare class TitledEditor extends CustomEditor {
|
|
23
|
+
/** Session-name chip painted into the LEFT of the top border. Empty → plain. */
|
|
24
|
+
title: string;
|
|
25
|
+
/** Pre-styled context string painted into the RIGHT of the top border (cwd /
|
|
26
|
+
* branch / git status). Already colorized by the caller; empty → omitted. */
|
|
27
|
+
info: string;
|
|
28
|
+
/** Paint the chip solid so the name reads as a label sitting on the border
|
|
29
|
+
* rule. Defaults to the reverse-video chip; attach-cmd swaps in a
|
|
30
|
+
* thinking-colored background (bold white text) on each state update. */
|
|
31
|
+
titleStyle: (s: string) => string;
|
|
32
|
+
render(width: number): string[];
|
|
33
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// titled-editor.ts — the `crtr attach` input editor with two bits of chrome pi's
|
|
2
|
+
// stock editor doesn't give us:
|
|
3
|
+
// 1. the session name painted INTO the top border as a solid-background chip,
|
|
4
|
+
// so it stays pinned to the editor instead of scrolling off as a free badge;
|
|
5
|
+
// 2. a border color that tracks the agent's thinking level (set by attach-cmd
|
|
6
|
+
// on each state update), mirroring pi's `getThinkingBorderColor`.
|
|
7
|
+
// Both are pure render-layer chrome; nothing here touches the socket or session.
|
|
8
|
+
import { CustomEditor } from '@earendil-works/pi-coding-agent';
|
|
9
|
+
import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
|
|
10
|
+
/** Per-level border/chip color as a 24-bit RGB triple — ONE blue-violet hue at
|
|
11
|
+
* CONSTANT perceived lightness (luma ≈ 120) whose SATURATION alone climbs with
|
|
12
|
+
* the budget: a near-gray blue at `minimal` deepening to a vivid violet at
|
|
13
|
+
* `xhigh`, never getting lighter or darker — only more colorful. (pi paints its
|
|
14
|
+
* own `thinking*` theme colors off its public palette surface, so we own this
|
|
15
|
+
* ramp directly — truecolor, not the 16-color ANSI hues, so the saturation
|
|
16
|
+
* climb actually reads.) `off` falls back to the caller's default border color. */
|
|
17
|
+
const THINKING_RGB = {
|
|
18
|
+
off: undefined, // default border color
|
|
19
|
+
minimal: [125, 115, 135], // near-gray blue-violet
|
|
20
|
+
low: [129, 109, 151], // muted blue-violet
|
|
21
|
+
medium: [134, 103, 168], // violet
|
|
22
|
+
high: [139, 98, 184], // deeper violet
|
|
23
|
+
xhigh: [144, 92, 200], // vivid violet
|
|
24
|
+
};
|
|
25
|
+
/** The default (thinking `off`) title chip: reverse video + a space of padding
|
|
26
|
+
* each side, so the name reads as a label sitting on the border rule. Used as
|
|
27
|
+
* the fallback when no thinking color applies. */
|
|
28
|
+
export const defaultTitleStyle = (s) => `\x1b[7m ${s} \x1b[27m`;
|
|
29
|
+
/** Resolve the editor border colorizer for a thinking level. Unknown / `off` →
|
|
30
|
+
* the supplied `fallback` (the theme's default border color). */
|
|
31
|
+
export function thinkingBorderColor(level, fallback) {
|
|
32
|
+
const rgb = level === undefined ? undefined : THINKING_RGB[level];
|
|
33
|
+
if (rgb === undefined)
|
|
34
|
+
return fallback;
|
|
35
|
+
const [r, g, b] = rgb;
|
|
36
|
+
return (s) => `\x1b[38;2;${r};${g};${b}m${s}\x1b[39m`;
|
|
37
|
+
}
|
|
38
|
+
/** Resolve the title-chip styler for a thinking level: the level's color as the
|
|
39
|
+
* chip BACKGROUND with bold white text (a space of padding each side), so the
|
|
40
|
+
* session name reads as a solid label in the same hue as the border. Unknown /
|
|
41
|
+
* `off` → the supplied `fallback` (the reverse-video default chip). */
|
|
42
|
+
export function thinkingTitleStyle(level, fallback) {
|
|
43
|
+
const rgb = level === undefined ? undefined : THINKING_RGB[level];
|
|
44
|
+
if (rgb === undefined)
|
|
45
|
+
return fallback;
|
|
46
|
+
const [r, g, b] = rgb;
|
|
47
|
+
return (s) => `\x1b[48;2;${r};${g};${b}m\x1b[97m\x1b[1m ${s} \x1b[0m`;
|
|
48
|
+
}
|
|
49
|
+
/** Compose the replacement top-border line: solid title chip + border rule +
|
|
50
|
+
* info chip, never wider than `width` (pi-tui hard-crashes on an over-wide
|
|
51
|
+
* line). The info chip yields entirely when the chip leaves it almost no room,
|
|
52
|
+
* and truncates when it only partially fits. Exported pure for the overflow
|
|
53
|
+
* regression test. */
|
|
54
|
+
export function composeTopBorder(width, title, info, titleStyle, borderColor) {
|
|
55
|
+
const chip = title ? titleStyle(truncateToWidth(title, Math.max(1, width - 4), '…')) : '';
|
|
56
|
+
const chipW = visibleWidth(chip);
|
|
57
|
+
const avail = width - chipW;
|
|
58
|
+
let infoChip = '';
|
|
59
|
+
if (info && avail > 4) {
|
|
60
|
+
infoChip = visibleWidth(info) <= avail ? info : truncateToWidth(info, avail, '…');
|
|
61
|
+
}
|
|
62
|
+
const fill = Math.max(0, width - chipW - visibleWidth(infoChip));
|
|
63
|
+
return chip + borderColor('─'.repeat(fill)) + infoChip;
|
|
64
|
+
}
|
|
65
|
+
export class TitledEditor extends CustomEditor {
|
|
66
|
+
/** Session-name chip painted into the LEFT of the top border. Empty → plain. */
|
|
67
|
+
title = '';
|
|
68
|
+
/** Pre-styled context string painted into the RIGHT of the top border (cwd /
|
|
69
|
+
* branch / git status). Already colorized by the caller; empty → omitted. */
|
|
70
|
+
info = '';
|
|
71
|
+
/** Paint the chip solid so the name reads as a label sitting on the border
|
|
72
|
+
* rule. Defaults to the reverse-video chip; attach-cmd swaps in a
|
|
73
|
+
* thinking-colored background (bold white text) on each state update. */
|
|
74
|
+
titleStyle = defaultTitleStyle;
|
|
75
|
+
render(width) {
|
|
76
|
+
const lines = super.render(width);
|
|
77
|
+
if ((this.title || this.info) && lines.length > 0) {
|
|
78
|
+
// Replace the stock top border; dashes in the current (thinking-aware)
|
|
79
|
+
// border color.
|
|
80
|
+
lines[0] = composeTopBorder(width, this.title, this.info, this.titleStyle, (s) => this.borderColor(s));
|
|
81
|
+
}
|
|
82
|
+
return lines;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
2
|
import type { NodeRow } from '../../core/canvas/types.js';
|
|
3
|
-
import { type BrokerToClient, type ClientToBroker } from '../../core/runtime/broker-protocol.js';
|
|
3
|
+
import { type BrokerDataFrame, type BrokerToClient, type ClientToBroker, type DequeueFrame, type GetSettingsFrame, type GetTreeFrame, type ListModelsFrame, type ListScopedModelsFrame, type ListSessionsFrame } from '../../core/runtime/broker-protocol.js';
|
|
4
|
+
/** A correlated read-op (or `dequeue`) request MINUS the client-chosen `id` —
|
|
5
|
+
* {@link ViewSocketClient.request} mints the `id`, sends the frame, and resolves
|
|
6
|
+
* with the matching `data` reply. The picker/operator code builds these; the
|
|
7
|
+
* socket owns the correlation token so callers never hand-roll one. */
|
|
8
|
+
export type ReadOpRequest = Omit<ListModelsFrame, 'id'> | Omit<ListSessionsFrame, 'id'> | Omit<GetTreeFrame, 'id'> | Omit<GetSettingsFrame, 'id'> | Omit<ListScopedModelsFrame, 'id'> | Omit<DequeueFrame, 'id'>;
|
|
4
9
|
/** Surfaced when the node has no reachable broker at connect time (no socket
|
|
5
10
|
* file, or a stale socket with nothing listening). The command catches this to
|
|
6
11
|
* exit non-zero with a focus/revive hint. */
|
|
@@ -36,7 +41,19 @@ export declare class ViewSocketClient extends EventEmitter {
|
|
|
36
41
|
private socket;
|
|
37
42
|
private decoder;
|
|
38
43
|
private closeEmitted;
|
|
44
|
+
/** In-flight correlated read-ops, keyed by the `id` minted in {@link request}.
|
|
45
|
+
* Resolved by the matching `data` frame / rejected by the matching `error`
|
|
46
|
+
* frame in {@link onData}, the request's timeout, or socket teardown. */
|
|
47
|
+
private pending;
|
|
39
48
|
constructor(nodeId: string);
|
|
49
|
+
/** Issue a correlated read-op and resolve with the broker's `data` reply (or
|
|
50
|
+
* reject on the correlated `error`, a timeout, or socket teardown). Mints the
|
|
51
|
+
* `id`, sends `{...frame, id}`, and parks a resolver consumed by {@link onData}.
|
|
52
|
+
* The reply is narrowed by the caller on its `kind`. */
|
|
53
|
+
request(frame: ReadOpRequest): Promise<BrokerDataFrame>;
|
|
54
|
+
/** Reject + clear every in-flight request (socket gone / decode error) so a
|
|
55
|
+
* picker fetch never hangs past the connection it rode on. */
|
|
56
|
+
private rejectAllPending;
|
|
40
57
|
/** The broker binds `join(nodeDir(id), 'view.sock')` — resolve it the same way. */
|
|
41
58
|
get socketPath(): string;
|
|
42
59
|
/** Open the connection. Wire up listeners; emits `connect` on success or
|