@north-light/crouter 0.3.173 → 0.3.175
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/bin/runtime-selector.mjs +8 -1
- package/dist/api/__tests__/{client.test.js → serial/client.test.js} +2 -135
- package/dist/api/client.d.ts +12 -21
- package/dist/api/client.js +22 -31
- package/dist/api/dto/broker-ops.d.ts +158 -0
- package/dist/api/dto/broker-ops.js +6 -0
- package/dist/api/dto/crons.d.ts +6 -0
- package/dist/api/dto/human.d.ts +7 -1
- package/dist/api/dto/inbox.d.ts +1 -71
- package/dist/api/dto/inbox.js +1 -9
- package/dist/api/dto/nodes.d.ts +6 -7
- package/dist/api/dto/reviews.d.ts +1 -11
- package/dist/api/routes.d.ts +7 -4
- package/dist/api/routes.js +8 -5
- package/dist/build-root.js +3 -3
- package/dist/builtin-memory/04-base-worker.md +1 -4
- package/dist/builtin-memory/05-kinds/advisor/00-base.md +1 -5
- package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +2 -7
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -6
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -6
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -4
- package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -2
- package/dist/builtin-memory/05-kinds/general/00-base.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -6
- package/dist/builtin-memory/05-kinds/plan/reviewers/00-base.md +1 -2
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -3
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -4
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +2 -3
- package/dist/builtin-memory/05-kinds/spec/00-base.md +1 -2
- package/dist/builtin-memory/advisor/council.md +1 -6
- package/dist/builtin-memory/design.md +2 -7
- package/dist/builtin-memory/development.md +9 -14
- package/dist/builtin-memory/internal/marketplaces.md +2 -6
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/plugins.md +2 -8
- package/dist/builtin-memory/internal/storage-tiers.md +1 -2
- package/dist/builtin-memory/testing.md +41 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{provider-rotation.test.ts → serial/provider-rotation.test.ts} +27 -179
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +5 -15
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +1 -28
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +43 -88
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +9 -1
- package/dist/cli.js +2 -2
- package/dist/clients/attach/__tests__/attach-remote-readonly.test.js +37 -131
- package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +71 -152
- package/dist/clients/attach/__tests__/context-message.test.js +16 -174
- package/dist/clients/attach/__tests__/crtr-output.test.js +36 -2069
- package/dist/clients/attach/__tests__/diagram.test.js +2 -2
- package/dist/clients/attach/__tests__/edit-diff.test.js +0 -4
- package/dist/clients/attach/__tests__/file-review-focus.test.js +2 -1
- package/dist/clients/attach/__tests__/group-activity.test.js +40 -229
- package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.js +26 -76
- package/dist/clients/attach/__tests__/prompt-preview.test.js +4 -70
- package/dist/clients/attach/__tests__/ref-autocomplete.test.js +11 -125
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +14 -205
- package/dist/clients/attach/command.d.ts +2 -1
- package/dist/clients/attach/command.js +81 -76
- package/dist/clients/attach/input/capabilities.d.ts +27 -0
- package/dist/clients/attach/input/capabilities.js +96 -0
- package/dist/clients/attach/input/controller.d.ts +20 -182
- package/dist/clients/attach/input/controller.js +128 -537
- package/dist/clients/attach/input/overlay-owner.d.ts +41 -0
- package/dist/clients/attach/input/overlay-owner.js +199 -0
- package/dist/clients/attach/overlays/file-review.d.ts +0 -4
- package/dist/clients/attach/overlays/file-review.js +13 -5
- package/dist/clients/attach/overlays/graph.js +2 -10
- package/dist/clients/attach/render/chat-view.d.ts +4 -3
- package/dist/clients/attach/render/chat-view.js +39 -33
- package/dist/clients/attach/render/group-activity.d.ts +1 -0
- package/dist/clients/attach/render/group-activity.js +20 -13
- package/dist/clients/attach/render/group-recap.js +12 -3
- package/dist/clients/attach/session/connection.js +9 -15
- package/dist/clients/attach/session/editor-inventory.d.ts +6 -5
- package/dist/clients/attach/session/editor-inventory.js +7 -8
- package/dist/clients/attach/session/file-links.d.ts +13 -0
- package/dist/clients/attach/session/file-links.js +107 -0
- package/dist/clients/attach/session/frame.d.ts +3 -0
- package/dist/clients/attach/session/frame.js +10 -1
- package/dist/clients/attach/session/frames.js +2 -3
- package/dist/clients/attach/session/input-wiring.d.ts +4 -1
- package/dist/clients/attach/session/input-wiring.js +15 -14
- package/dist/clients/attach/session/mouse.d.ts +6 -1
- package/dist/clients/attach/session/mouse.js +8 -5
- package/dist/clients/attach/session/profile-files.js +18 -12
- package/dist/clients/attach/session/reconnect.js +1 -1
- package/dist/clients/attach/session/state-sync.js +1 -2
- package/dist/clients/attach/session/surface-host.d.ts +3 -2
- package/dist/clients/attach/session/surface-host.js +4 -4
- package/dist/clients/attach/slash/dispatch.d.ts +8 -31
- package/dist/clients/attach/slash/dispatch.js +34 -81
- package/dist/clients/attach/viewer.js +820 -860
- package/dist/clients/conversation/projection.d.ts +79 -0
- package/dist/clients/conversation/projection.js +341 -0
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +159 -0
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +151 -0
- package/dist/clients/inbox/controller.d.ts +0 -4
- package/dist/clients/inbox/controller.js +0 -2
- package/dist/clients/inbox/review/__tests__/review-core.test.js +4 -293
- package/dist/clients/inbox/review/__tests__/teardown-notice.test.js +100 -0
- package/dist/clients/inbox/review/comments-client.d.ts +3 -2
- package/dist/clients/inbox/review/comments-client.js +12 -12
- package/dist/clients/inbox/review/companion-pane.d.ts +30 -0
- package/dist/clients/inbox/review/companion-pane.js +86 -0
- package/dist/clients/inbox/review/document-surface.d.ts +182 -0
- package/dist/clients/inbox/review/{document-region.js → document-surface.js} +252 -48
- package/dist/clients/inbox/review/frame.js +5 -4
- package/dist/clients/inbox/review/launch.d.ts +8 -8
- package/dist/clients/inbox/review/launch.js +52 -92
- package/dist/clients/inbox/review/nvim.d.ts +2 -0
- package/dist/clients/inbox/review/nvim.js +14 -12
- package/dist/clients/inbox/review/review-client.d.ts +12 -8
- package/dist/clients/inbox/review/review-client.js +46 -35
- package/dist/clients/inbox/review-adapter.d.ts +3 -10
- package/dist/clients/inbox/review-adapter.js +27 -19
- package/dist/clients/inbox/surface.d.ts +0 -4
- package/dist/clients/inbox/surface.js +0 -2
- package/dist/clients/surfaces/host.d.ts +11 -3
- package/dist/commands/__tests__/api-client-cold-start-diagnostic.test.js +7 -34
- package/dist/commands/__tests__/human.test.js +11 -322
- package/dist/commands/api-client.d.ts +5 -5
- package/dist/commands/api-client.js +6 -85
- package/dist/commands/canvas-browse.js +3 -29
- package/dist/commands/canvas-config.js +1 -1
- package/dist/commands/canvas-use.js +3 -3
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/cron.js +5 -1
- package/dist/commands/human/queue.d.ts +0 -4
- package/dist/commands/human/queue.js +7 -40
- package/dist/commands/memory/list.d.ts +14 -0
- package/dist/commands/memory/list.js +35 -31
- package/dist/commands/memory/read.d.ts +7 -0
- package/dist/commands/memory/read.js +14 -8
- package/dist/commands/memory/write.js +13 -12
- package/dist/commands/node/bash.d.ts +1 -0
- package/dist/commands/node/bash.js +198 -0
- package/dist/commands/node/create.d.ts +23 -0
- package/dist/commands/node/create.js +207 -0
- package/dist/commands/node/inspect.d.ts +2 -0
- package/dist/commands/node/inspect.js +302 -0
- package/dist/commands/node/lifecycle.d.ts +4 -0
- package/dist/commands/node/lifecycle.js +415 -0
- package/dist/commands/node/message.d.ts +1 -0
- package/dist/commands/node/message.js +164 -0
- package/dist/commands/node/pyramid.d.ts +2 -0
- package/dist/commands/node/pyramid.js +79 -0
- package/dist/commands/node/subscription.d.ts +1 -0
- package/dist/commands/node/subscription.js +82 -0
- package/dist/commands/node/wait.d.ts +1 -0
- package/dist/commands/node/wait.js +159 -0
- package/dist/commands/node-lifecycle-revive.js +10 -57
- package/dist/commands/node.d.ts +0 -42
- package/dist/commands/node.js +11 -2065
- package/dist/commands/search.js +1 -1
- package/dist/commands/surface/node/cycle.d.ts +1 -0
- package/dist/commands/surface/node/cycle.js +83 -0
- package/dist/commands/surface/node/focus.d.ts +1 -0
- package/dist/commands/surface/node/focus.js +67 -0
- package/dist/commands/surface/node/id.d.ts +1 -0
- package/dist/commands/surface/node/id.js +38 -0
- package/dist/commands/surface/node/pin.d.ts +1 -0
- package/dist/commands/surface/node/pin.js +42 -0
- package/dist/commands/surface/node/placement.d.ts +46 -0
- package/dist/commands/surface/node/placement.js +124 -0
- package/dist/commands/surface-node.js +4 -1
- package/dist/commands/surface-reload.js +1 -2
- package/dist/commands/surface-tmux-spread.js +1 -1
- package/dist/commands/surface.js +5 -20
- package/dist/commands/sys/__tests__/setup-core.test.js +18 -271
- package/dist/commands/sys/config.js +6 -6
- package/dist/commands/sys/logs.js +1 -1
- package/dist/commands/sys/panels/broker-limits-panel.d.ts +17 -0
- package/dist/commands/sys/panels/broker-limits-panel.js +104 -0
- package/dist/commands/sys/panels/dependencies-panel.d.ts +14 -0
- package/dist/commands/sys/panels/dependencies-panel.js +80 -0
- package/dist/commands/sys/panels/keybindings-panel.d.ts +56 -0
- package/dist/commands/sys/panels/keybindings-panel.js +703 -0
- package/dist/commands/sys/panels/keys-panel.d.ts +16 -0
- package/dist/commands/sys/panels/keys-panel.js +60 -0
- package/dist/commands/sys/panels/models-panel.d.ts +77 -0
- package/dist/commands/sys/panels/models-panel.js +647 -0
- package/dist/commands/sys/panels/panel.d.ts +45 -0
- package/dist/commands/sys/panels/panel.js +137 -0
- package/dist/commands/sys/panels/plugins-panel.d.ts +16 -0
- package/dist/commands/sys/panels/plugins-panel.js +80 -0
- package/dist/commands/sys/panels/prefs-panel.d.ts +21 -0
- package/dist/commands/sys/panels/prefs-panel.js +85 -0
- package/dist/commands/sys/panels/provider-panel.d.ts +62 -0
- package/dist/commands/sys/panels/provider-panel.js +538 -0
- package/dist/commands/sys/settings-shell.d.ts +55 -0
- package/dist/commands/sys/settings-shell.js +400 -0
- package/dist/commands/sys/settings.d.ts +2 -0
- package/dist/commands/sys/settings.js +31 -0
- package/dist/commands/sys/setup-core.d.ts +3 -33
- package/dist/commands/sys/setup-core.js +27 -78
- package/dist/commands/sys/setup-settings-runner.d.ts +24 -0
- package/dist/commands/sys/setup-settings-runner.js +63 -0
- package/dist/commands/sys/setup-wizard.d.ts +26 -125
- package/dist/commands/sys/setup-wizard.js +209 -2473
- package/dist/commands/sys/setup.js +5 -7
- package/dist/commands/sys.js +3 -2
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +50 -0
- package/dist/core/__tests__/broker-launch-candidates.test.js +20 -9
- package/dist/core/__tests__/broker-preflight.test.js +1 -1
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +108 -27
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +14 -0
- package/dist/core/__tests__/canvas.test.js +2 -36
- package/dist/core/__tests__/close.test.js +14 -3
- package/dist/core/__tests__/context-intro.test.js +32 -143
- package/dist/core/__tests__/cron-broker-capacity.test.js +22 -3
- package/dist/core/__tests__/daemon-boot.test.js +2 -20
- package/dist/core/__tests__/daemon-wedge.test.js +1 -1
- package/dist/core/__tests__/dead-node-policy-table.test.js +1 -18
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +2 -2
- package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
- package/dist/core/__tests__/focuses.test.js +4 -98
- package/dist/core/__tests__/helpers/broker-clients.d.ts +0 -3
- package/dist/core/__tests__/helpers/broker-clients.js +0 -14
- package/dist/core/__tests__/helpers/harness.d.ts +0 -8
- package/dist/core/__tests__/helpers/harness.js +7 -87
- package/dist/core/__tests__/helpers/viewer-panes.js +1 -1
- package/dist/core/__tests__/inline-memory-refs.test.js +5 -134
- package/dist/core/__tests__/kickoff.test.js +1 -1
- package/dist/core/__tests__/memory-resolver-precedence.test.js +29 -1
- package/dist/core/__tests__/migration.test.js +35 -48
- package/dist/core/__tests__/on-read-dedup-resume.test.js +22 -12
- package/dist/core/__tests__/passive-subscription.test.js +26 -49
- package/dist/core/__tests__/preview-registry-sync.test.js +1 -2
- package/dist/core/__tests__/prune.test.js +94 -1
- package/dist/core/__tests__/review-model-floor.test.js +0 -32
- package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +255 -0
- package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.js +39 -7
- package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.js +30 -29
- package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.js +54 -5
- package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.js +134 -82
- package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.js +15 -4
- package/dist/core/__tests__/{full/idle-release-settlement.test.js → seam/dormancy-release.test.js} +69 -17
- package/dist/core/__tests__/{full/yield-message-transaction.test.js → seam/yield-refresh-transaction.test.js} +31 -9
- package/dist/core/__tests__/secrets.test.js +1 -7
- package/dist/core/__tests__/{broker-fork-seam.test.js → serial/broker-fork-seam.test.js} +58 -7
- package/dist/core/__tests__/{broker-sdk-wiring.test.js → serial/broker-sdk-wiring.test.js} +18 -162
- package/dist/core/__tests__/{broker-snapshot-history.test.js → serial/broker-snapshot-history.test.js} +9 -9
- package/dist/core/__tests__/{command-plugins.test.js → serial/command-plugins.test.js} +105 -15
- package/dist/core/__tests__/{deferred-no-wake.test.js → serial/deferred-no-wake.test.js} +23 -20
- package/dist/core/__tests__/{flagship-lifecycle.test.js → serial/flagship-lifecycle.test.js} +7 -7
- package/dist/core/__tests__/{host-teardown-process-group.test.js → serial/host-teardown-process-group.test.js} +6 -6
- package/dist/core/__tests__/{human-deliver-e2e.test.js → serial/human-deliver-e2e.test.js} +12 -12
- package/dist/core/__tests__/{live-mutation.test.js → serial/live-mutation.test.js} +5 -5
- package/dist/core/__tests__/{refresh-stall-recycle.test.js → serial/refresh-stall-recycle.test.js} +5 -14
- package/dist/core/__tests__/{revive.test.js → serial/revive.test.js} +15 -100
- package/dist/core/__tests__/{spawn-root.test.js → serial/spawn-root.test.js} +32 -15
- package/dist/core/__tests__/serial/subscription-delivery.test.js +126 -0
- package/dist/core/__tests__/serial/tmux-surface.test.js +256 -0
- package/dist/core/__tests__/{worktree.test.js → serial/worktree.test.js} +15 -15
- package/dist/core/__tests__/session-model.test.js +0 -18
- package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +9 -0
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +50 -0
- package/dist/core/broker-client/__tests__/transport-relay.test.js +15 -16
- package/dist/core/broker-client/__tests__/transport-socket.test.js +28 -0
- package/dist/core/broker-client/client.d.ts +2 -2
- package/dist/core/broker-client/client.js +4 -7
- package/dist/core/broker-client/transport-relay.d.ts +13 -19
- package/dist/core/broker-client/transport-relay.js +19 -34
- package/dist/core/broker-client/transport-socket.d.ts +2 -2
- package/dist/core/broker-client/transport-socket.js +9 -2
- package/dist/core/broker-client/transport.d.ts +7 -3
- package/dist/core/broker-client/transport.js +4 -4
- package/dist/core/canvas/__tests__/attention.test.js +1 -18
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +7 -64
- package/dist/core/canvas/__tests__/remote-transport.test.js +2 -3
- package/dist/core/canvas/__tests__/render-remote.test.js +7 -52
- package/dist/core/canvas/browse/app.d.ts +2 -17
- package/dist/core/canvas/browse/app.js +16 -88
- package/dist/core/canvas/browse/model.d.ts +0 -4
- package/dist/core/canvas/browse/model.js +1 -12
- package/dist/core/canvas/browse/render.js +6 -3
- package/dist/core/canvas/crons.d.ts +8 -5
- package/dist/core/canvas/crons.js +7 -6
- package/dist/core/canvas/extensions.d.ts +7 -3
- package/dist/core/canvas/extensions.js +16 -4
- package/dist/core/canvas/nav-model.d.ts +0 -4
- package/dist/core/canvas/nav-model.js +2 -14
- package/dist/core/canvas/node-order.d.ts +3 -0
- package/dist/core/canvas/node-order.js +12 -0
- package/dist/core/canvas/remote-canvas-source.d.ts +3 -6
- package/dist/core/canvas/remote-canvas-source.js +8 -15
- package/dist/core/command.js +11 -4
- package/dist/core/config.js +3 -1
- package/dist/core/events/envelope.js +1 -1
- package/dist/core/events/read.js +7 -21
- package/dist/core/events/source.d.ts +0 -2
- package/dist/core/events/source.js +0 -16
- package/dist/core/events/types.d.ts +1 -1
- package/dist/core/fault-classifier.d.ts +1 -1
- package/dist/core/feed/passive.d.ts +0 -11
- package/dist/core/feed/passive.js +1 -62
- package/dist/core/human/__tests__/{inbox-core.test.js → serial/inbox-core.test.js} +6 -6
- package/dist/core/keybindings/__tests__/resolve.test.js +1 -96
- package/dist/core/keybindings/background-bash.js +3 -4
- package/dist/core/keybindings/catalog.js +1 -1
- package/dist/core/memory/inline-ref-grammar.js +3 -3
- package/dist/core/memory-resolver.d.ts +11 -25
- package/dist/core/memory-resolver.js +51 -33
- package/dist/core/model-routes.d.ts +15 -0
- package/dist/core/model-routes.js +12 -0
- package/dist/core/pool-credential-store.js +1 -1
- package/dist/core/preview-registry.js +8 -9
- package/dist/core/profiles/__tests__/root-profile.test.js +4 -14
- package/dist/core/review/realize.d.ts +0 -13
- package/dist/core/review/realize.js +0 -40
- package/dist/core/review/types.d.ts +1 -1
- package/dist/core/runtime/__tests__/engine-route.test.js +2 -2
- package/dist/core/runtime/bearings.js +1 -11
- package/dist/core/runtime/broker/client-registry.d.ts +33 -0
- package/dist/core/runtime/broker/client-registry.js +164 -0
- package/dist/core/runtime/broker/daemon-ops.d.ts +19 -0
- package/dist/core/runtime/broker/daemon-ops.js +49 -0
- package/dist/core/runtime/broker/event-projection.d.ts +42 -0
- package/dist/core/runtime/broker/event-projection.js +178 -0
- package/dist/core/runtime/broker/fault-retry.d.ts +39 -0
- package/dist/core/runtime/broker/fault-retry.js +277 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +238 -0
- package/dist/core/runtime/broker/frame-dispatch.js +1537 -0
- package/dist/core/runtime/broker/inbox.d.ts +22 -0
- package/dist/core/runtime/broker/inbox.js +167 -0
- package/dist/core/runtime/broker/passive.d.ts +16 -0
- package/dist/core/runtime/broker/passive.js +44 -0
- package/dist/core/runtime/broker/read-ops.d.ts +23 -0
- package/dist/core/runtime/broker/read-ops.js +150 -0
- package/dist/core/runtime/broker/rebind.d.ts +74 -0
- package/dist/core/runtime/broker/rebind.js +211 -0
- package/dist/core/runtime/broker/tool-groups.d.ts +39 -0
- package/dist/core/runtime/broker/tool-groups.js +244 -0
- package/dist/core/runtime/broker-extension-render.d.ts +11 -0
- package/dist/core/runtime/broker-extension-render.js +181 -0
- package/dist/core/runtime/broker-persona-guidance.d.ts +13 -0
- package/dist/core/runtime/broker-persona-guidance.js +58 -0
- package/dist/core/runtime/broker-protocol.d.ts +1 -1
- package/dist/core/runtime/broker-protocol.js +1 -1
- package/dist/core/runtime/broker.d.ts +5 -196
- package/dist/core/runtime/broker.js +276 -3059
- package/dist/core/runtime/canvas-extensions.js +11 -2
- package/dist/core/runtime/fault.js +1 -1
- package/dist/core/runtime/model-selection.d.ts +9 -0
- package/dist/core/runtime/model-selection.js +12 -0
- package/dist/core/runtime/model-swap.d.ts +3 -3
- package/dist/core/runtime/model-swap.js +33 -25
- package/dist/core/runtime/placement-tmux.d.ts +3 -14
- package/dist/core/runtime/placement-tmux.js +11 -80
- package/dist/core/runtime/placement.d.ts +4 -2
- package/dist/core/runtime/placement.js +7 -12
- package/dist/core/runtime/promote.d.ts +14 -1
- package/dist/core/runtime/promote.js +1 -1
- package/dist/core/runtime/reset.d.ts +1 -1
- package/dist/core/runtime/roadmap.js +1 -1
- package/dist/core/runtime/spawn.d.ts +8 -0
- package/dist/core/runtime/spawn.js +36 -6
- package/dist/core/runtime/stop-guard.d.ts +4 -3
- package/dist/core/runtime/stop-guard.js +36 -18
- package/dist/core/runtime/stop-signals.d.ts +4 -1
- package/dist/core/runtime/stream-watchdog.js +4 -5
- package/dist/core/runtime/structured-output.d.ts +15 -2
- package/dist/core/runtime/structured-output.js +35 -12
- package/dist/core/runtime/tmux-bindings.d.ts +25 -0
- package/dist/core/runtime/tmux-bindings.js +682 -0
- package/dist/core/runtime/tmux-chrome.d.ts +2 -1
- package/dist/core/runtime/tmux-chrome.js +3 -2
- package/dist/core/runtime/{tmux.d.ts → tmux-driver.d.ts} +18 -26
- package/dist/core/runtime/tmux-driver.js +598 -0
- package/dist/core/runtime/view-socket.d.ts +14 -0
- package/dist/core/runtime/view-socket.js +83 -0
- package/dist/core/session-model/session-state.d.ts +1 -2
- package/dist/core/session-model/session-state.js +5 -8
- package/dist/core/spawn.d.ts +1 -1
- package/dist/core/spawn.js +2 -2
- package/dist/core/substrate/gate.d.ts +1 -1
- package/dist/core/substrate/on-read-pure.d.ts +7 -0
- package/dist/core/substrate/on-read-pure.js +272 -0
- package/dist/core/termrender/termrender.js +32 -2
- package/dist/core/user-settings.d.ts +23 -1
- package/dist/core/user-settings.js +18 -1
- package/dist/daemon/api/__tests__/node-model-validation.test.js +134 -0
- package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +98 -0
- package/dist/daemon/api/__tests__/{full → seam}/api-server.test.js +6 -48
- package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.js +17 -44
- package/dist/daemon/api/__tests__/{full/push-parity.test.js → seam/leaf-api-parity.test.js} +61 -42
- package/dist/daemon/api/bridge.js +40 -81
- package/dist/daemon/api/handlers/broker-ops.d.ts +2 -0
- package/dist/daemon/api/handlers/broker-ops.js +383 -0
- package/dist/daemon/api/handlers/crons.js +9 -4
- package/dist/daemon/api/handlers/files.js +6 -7
- package/dist/daemon/api/handlers/inbox.js +2 -340
- package/dist/daemon/api/handlers/nodes.d.ts +10 -1
- package/dist/daemon/api/handlers/nodes.js +74 -39
- package/dist/daemon/api/handlers/review-comments.js +5 -1
- package/dist/daemon/api/handlers/reviews.js +0 -19
- package/dist/daemon/api/map.js +25 -4
- package/dist/daemon/api/router.js +2 -3
- package/dist/daemon/api/server.js +5 -7
- package/dist/daemon/cron-run.d.ts +4 -0
- package/dist/daemon/cron-run.js +30 -13
- package/dist/daemon/crtrd.d.ts +3 -58
- package/dist/daemon/crtrd.js +28 -1244
- package/dist/daemon/manage.js +0 -1
- package/dist/daemon/reconcilers/broker-supervision.d.ts +50 -0
- package/dist/daemon/reconcilers/broker-supervision.js +549 -0
- package/dist/daemon/reconcilers/cron-lane.d.ts +10 -0
- package/dist/daemon/reconcilers/cron-lane.js +32 -0
- package/dist/daemon/reconcilers/dormant-inbox.d.ts +18 -0
- package/dist/daemon/reconcilers/dormant-inbox.js +143 -0
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +22 -0
- package/dist/daemon/reconcilers/storage-maintenance.js +213 -0
- package/dist/daemon/review/finish.js +2 -5
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +34 -53
- package/dist/pi-extensions/__tests__/canvas-recap.test.js +16 -113
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +72 -99
- package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +29 -12
- package/dist/pi-extensions/broker-local.d.ts +20 -0
- package/dist/pi-extensions/broker-local.js +89 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +3 -3
- package/dist/pi-extensions/canvas-context-intro.js +22 -11
- package/dist/pi-extensions/canvas-doc-substrate.js +17 -34
- package/dist/pi-extensions/canvas-goal-capture.js +7 -12
- package/dist/pi-extensions/canvas-inbox-watcher.js +124 -139
- package/dist/pi-extensions/canvas-passive-context.d.ts +3 -3
- package/dist/pi-extensions/canvas-passive-context.js +22 -18
- package/dist/pi-extensions/canvas-recap.d.ts +2 -2
- package/dist/pi-extensions/canvas-recap.js +48 -21
- package/dist/pi-extensions/canvas-review-boundary.js +10 -4
- package/dist/pi-extensions/canvas-stophook.d.ts +0 -5
- package/dist/pi-extensions/canvas-stophook.js +149 -431
- package/dist/pi-extensions/canvas-structured-output.js +52 -30
- package/dist/pi-extensions/envelope-merge.d.ts +13 -0
- package/dist/pi-extensions/envelope-merge.js +29 -0
- package/dist/shared/working-activity.d.ts +3 -2
- package/dist/shared/working-activity.js +3 -2
- package/dist/types.d.ts +6 -5
- package/dist/types.js +2 -2
- package/docs/compat/hearth-crtr-v1.md +4 -12
- package/docs/compat/hearth-crtr-v2.md +3 -11
- package/docs/compat/hearth-crtr-v3.md +4 -12
- package/docs/compat/hearth-crtr-v4.md +3 -11
- package/docs/compat/hearth-crtr-v5.md +3 -11
- package/docs/public-api.md +3 -7
- package/package.json +7 -49
- package/runtime.lock.json +2766 -14533
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/claude-plugin-commands.test.ts +0 -49
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/memory-slash-commands.test.ts +0 -194
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/statusline.test.ts +0 -68
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/test.ts +0 -119
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +0 -439
- package/dist/clients/attach/__tests__/attach-keybindings.test.js +0 -170
- package/dist/clients/attach/__tests__/crtr-output-coverage.test.js +0 -57
- package/dist/clients/attach/__tests__/editor-frame-title.test.js +0 -19
- package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +0 -32
- package/dist/clients/attach/__tests__/markdown-headings.test.js +0 -35
- package/dist/clients/attach/__tests__/session-identity-refresh.test.js +0 -32
- package/dist/clients/conversation/conversation.d.ts +0 -65
- package/dist/clients/conversation/conversation.js +0 -318
- package/dist/clients/conversation/frames.d.ts +0 -30
- package/dist/clients/conversation/frames.js +0 -52
- package/dist/clients/conversation/region.d.ts +0 -51
- package/dist/clients/conversation/region.js +0 -265
- package/dist/clients/inbox/__tests__/inbox-controller.test.js +0 -247
- package/dist/clients/inbox/__tests__/mount-panel.test.js +0 -776
- package/dist/clients/inbox/review/__tests__/remap.test.js +0 -196
- package/dist/clients/inbox/review/columns.d.ts +0 -22
- package/dist/clients/inbox/review/columns.js +0 -40
- package/dist/clients/inbox/review/composition.d.ts +0 -60
- package/dist/clients/inbox/review/composition.js +0 -278
- package/dist/clients/inbox/review/document-region.d.ts +0 -120
- package/dist/clients/surfaces/region.d.ts +0 -16
- package/dist/clients/web/__tests__/push-engine.test.js +0 -158
- package/dist/clients/web/__tests__/push-registry.test.d.ts +0 -1
- package/dist/clients/web/__tests__/push-registry.test.js +0 -177
- package/dist/clients/web/dev-server.d.ts +0 -7
- package/dist/clients/web/dev-server.js +0 -46
- package/dist/clients/web/events.d.ts +0 -19
- package/dist/clients/web/events.js +0 -163
- package/dist/clients/web/push-engine.d.ts +0 -44
- package/dist/clients/web/push-engine.js +0 -125
- package/dist/clients/web/push-registry.d.ts +0 -85
- package/dist/clients/web/push-registry.js +0 -373
- package/dist/clients/web/server.d.ts +0 -19
- package/dist/clients/web/server.js +0 -661
- package/dist/clients/web/web-client/shared/protocol.d.ts +0 -214
- package/dist/clients/web/web-client/shared/protocol.js +0 -8
- package/dist/clients/web/web-cmd.d.ts +0 -2
- package/dist/clients/web/web-cmd.js +0 -141
- package/dist/commands/__tests__/api-canvas-source.test.d.ts +0 -1
- package/dist/commands/__tests__/api-canvas-source.test.js +0 -37
- package/dist/commands/__tests__/canvas-config.test.d.ts +0 -1
- package/dist/commands/__tests__/canvas-config.test.js +0 -107
- package/dist/commands/__tests__/node-no-kickoff-flag.test.d.ts +0 -1
- package/dist/commands/__tests__/node-no-kickoff-flag.test.js +0 -73
- package/dist/commands/__tests__/node-worktree-flag.test.d.ts +0 -1
- package/dist/commands/__tests__/node-worktree-flag.test.js +0 -49
- package/dist/commands/__tests__/search-contents.test.d.ts +0 -1
- package/dist/commands/__tests__/search-contents.test.js +0 -52
- package/dist/commands/memory/__tests__/lint.test.d.ts +0 -1
- package/dist/commands/memory/__tests__/lint.test.js +0 -15
- package/dist/commands/sys/__tests__/config-broker-thresholds.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/config-broker-thresholds.test.js +0 -95
- package/dist/commands/sys/__tests__/config-keybindings.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/config-keybindings.test.js +0 -55
- package/dist/commands/sys/__tests__/helpers/setup-runtime.d.ts +0 -5
- package/dist/commands/sys/__tests__/helpers/setup-runtime.js +0 -19
- package/dist/commands/sys/__tests__/setup-front-door.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/setup-front-door.test.js +0 -102
- package/dist/commands/sys/__tests__/setup-models.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/setup-models.test.js +0 -205
- package/dist/commands/sys/__tests__/setup-providers-viewport.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/setup-providers-viewport.test.js +0 -155
- package/dist/commands/sys/__tests__/sync-deps.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/sync-deps.test.js +0 -179
- package/dist/commands/sys/__tests__/sync-import.test.d.ts +0 -1
- package/dist/commands/sys/__tests__/sync-import.test.js +0 -315
- package/dist/core/__tests__/base-worker-prompt.test.d.ts +0 -1
- package/dist/core/__tests__/base-worker-prompt.test.js +0 -57
- package/dist/core/__tests__/command-plugins-surfaces.test.d.ts +0 -1
- package/dist/core/__tests__/command-plugins-surfaces.test.js +0 -359
- package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +0 -1
- package/dist/core/__tests__/connection-reconnect-resume.test.js +0 -36
- package/dist/core/__tests__/finalization-migration.test.d.ts +0 -1
- package/dist/core/__tests__/finalization-migration.test.js +0 -38
- package/dist/core/__tests__/fixtures/extra-extension.d.ts +0 -1
- package/dist/core/__tests__/fixtures/extra-extension.js +0 -3
- package/dist/core/__tests__/fixtures/spawn-proof-target.test.d.ts +0 -1
- package/dist/core/__tests__/fixtures/spawn-proof-target.test.js +0 -15
- package/dist/core/__tests__/fork.test.d.ts +0 -1
- package/dist/core/__tests__/fork.test.js +0 -108
- package/dist/core/__tests__/full/broker-attach-limits.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-attach-limits.test.js +0 -147
- package/dist/core/__tests__/full/broker-deliver-ack.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-deliver-ack.test.js +0 -73
- package/dist/core/__tests__/full/broker-dialogs.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-dialogs.test.js +0 -180
- package/dist/core/__tests__/full/broker-display-snapshot.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-display-snapshot.test.js +0 -75
- package/dist/core/__tests__/full/broker-dormant-wake.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-dormant-wake.test.js +0 -130
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +0 -57
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +0 -1
- package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +0 -80
- package/dist/core/__tests__/full/cascade-close.test.d.ts +0 -1
- package/dist/core/__tests__/full/cascade-close.test.js +0 -240
- package/dist/core/__tests__/full/detach-focus.test.d.ts +0 -1
- package/dist/core/__tests__/full/detach-focus.test.js +0 -126
- package/dist/core/__tests__/full/idle-release-settlement.test.d.ts +0 -1
- package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.d.ts +0 -1
- package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.js +0 -107
- package/dist/core/__tests__/full/park-clock-release.test.d.ts +0 -1
- package/dist/core/__tests__/full/park-clock-release.test.js +0 -56
- package/dist/core/__tests__/full/yield-message-transaction.test.d.ts +0 -1
- package/dist/core/__tests__/memory-index-render.test.d.ts +0 -1
- package/dist/core/__tests__/memory-index-render.test.js +0 -60
- package/dist/core/__tests__/model-routes.test.d.ts +0 -1
- package/dist/core/__tests__/model-routes.test.js +0 -41
- package/dist/core/__tests__/on-read-identity.test.d.ts +0 -1
- package/dist/core/__tests__/on-read-identity.test.js +0 -61
- package/dist/core/__tests__/phase4-review-migration.test.d.ts +0 -1
- package/dist/core/__tests__/phase4-review-migration.test.js +0 -100
- package/dist/core/__tests__/prune-to-limit.test.d.ts +0 -1
- package/dist/core/__tests__/prune-to-limit.test.js +0 -126
- package/dist/core/__tests__/reset.test.d.ts +0 -1
- package/dist/core/__tests__/reset.test.js +0 -69
- package/dist/core/__tests__/spawn-no-kickoff.test.d.ts +0 -1
- package/dist/core/__tests__/spawn-no-kickoff.test.js +0 -115
- package/dist/core/__tests__/steer-note.test.d.ts +0 -1
- package/dist/core/__tests__/steer-note.test.js +0 -41
- package/dist/core/__tests__/subcommand-tier.test.d.ts +0 -1
- package/dist/core/__tests__/subcommand-tier.test.js +0 -99
- package/dist/core/__tests__/subscription-delivery.test.js +0 -211
- package/dist/core/__tests__/test-runner-cheap-sharding.test.d.ts +0 -1
- package/dist/core/__tests__/test-runner-cheap-sharding.test.js +0 -97
- package/dist/core/__tests__/tmux-surface.test.js +0 -484
- package/dist/core/__tests__/wake-bearings.test.d.ts +0 -1
- package/dist/core/__tests__/wake-bearings.test.js +0 -102
- package/dist/core/canvas/__tests__/remote-event-stream.test.d.ts +0 -1
- package/dist/core/canvas/__tests__/remote-event-stream.test.js +0 -146
- package/dist/core/canvas/__tests__/source-resolve.test.d.ts +0 -1
- package/dist/core/canvas/__tests__/source-resolve.test.js +0 -77
- package/dist/core/canvas/browse/__tests__/model.test.d.ts +0 -1
- package/dist/core/canvas/browse/__tests__/model.test.js +0 -290
- package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.d.ts +0 -1
- package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +0 -134
- package/dist/core/canvas/browse/__tests__/render.test.d.ts +0 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +0 -111
- package/dist/core/canvas/browse/rebuild-coalescer.d.ts +0 -7
- package/dist/core/canvas/browse/rebuild-coalescer.js +0 -61
- package/dist/core/canvas/remote-event-stream.d.ts +0 -24
- package/dist/core/canvas/remote-event-stream.js +0 -94
- package/dist/core/feed/__tests__/coalesce-human.test.d.ts +0 -1
- package/dist/core/feed/__tests__/coalesce-human.test.js +0 -36
- package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +0 -1
- package/dist/core/feed/__tests__/empty-node-guard.test.js +0 -45
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +0 -1
- package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +0 -84
- package/dist/core/human/__tests__/visible-paths.test.d.ts +0 -1
- package/dist/core/human/__tests__/visible-paths.test.js +0 -28
- package/dist/core/keybindings/__tests__/inbox-affordance.test.d.ts +0 -1
- package/dist/core/keybindings/__tests__/inbox-affordance.test.js +0 -38
- package/dist/core/review/__tests__/project.test.d.ts +0 -1
- package/dist/core/review/__tests__/project.test.js +0 -71
- package/dist/core/runtime/__tests__/command-expansion.test.d.ts +0 -1
- package/dist/core/runtime/__tests__/command-expansion.test.js +0 -25
- package/dist/core/runtime/__tests__/session-visibility.test.d.ts +0 -1
- package/dist/core/runtime/__tests__/session-visibility.test.js +0 -119
- package/dist/core/runtime/__tests__/spawn-at-given-id.test.d.ts +0 -1
- package/dist/core/runtime/__tests__/spawn-at-given-id.test.js +0 -34
- package/dist/core/runtime/tmux.js +0 -1314
- package/dist/daemon/api/__tests__/full/push-parity.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/full/worktree-parity.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/full/worktree-parity.test.js +0 -160
- package/dist/daemon/api/__tests__/inbox.test.d.ts +0 -1
- package/dist/daemon/api/__tests__/inbox.test.js +0 -489
- package/dist/web-client/apple-touch-icon.png +0 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
- package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
- package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
- package/dist/web-client/assets/index-CCPaLeW7.css +0 -2
- package/dist/web-client/assets/index-kyOWsnZK.js +0 -86
- package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
- package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
- package/dist/web-client/assets/workbox-window.prod.es5-Bd17z0YL.js +0 -2
- package/dist/web-client/icon-192.png +0 -0
- package/dist/web-client/icon-512.png +0 -0
- package/dist/web-client/icon-maskable-512.png +0 -0
- package/dist/web-client/index.html +0 -17
- package/dist/web-client/manifest.webmanifest +0 -1
- package/dist/web-client/sw.js +0 -1
- /package/dist/api/__tests__/{client.test.d.ts → serial/client.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{inbox-controller.test.d.ts → serial/inbox-controller.test.d.ts} +0 -0
- /package/dist/clients/inbox/__tests__/{mount-panel.test.d.ts → serial/mount-panel.test.d.ts} +0 -0
- /package/dist/clients/{attach/__tests__/attach-chrome-remote.test.d.ts → inbox/review/__tests__/teardown-notice.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/attach-keybindings.test.d.ts → core/__tests__/broker-extension-canvas-db-boundary.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/crtr-output-coverage.test.d.ts → core/__tests__/seam/broker-attach-multiclient.test.d.ts} +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.d.ts +0 -0
- /package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.d.ts +0 -0
- /package/dist/{clients/attach/__tests__/editor-frame-title.test.d.ts → core/__tests__/seam/dormancy-release.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/input-controller-extension-command.test.d.ts → core/__tests__/seam/yield-refresh-transaction.test.d.ts} +0 -0
- /package/dist/core/__tests__/{broker-fork-seam.test.d.ts → serial/broker-fork-seam.test.d.ts} +0 -0
- /package/dist/core/__tests__/{broker-sdk-wiring.test.d.ts → serial/broker-sdk-wiring.test.d.ts} +0 -0
- /package/dist/core/__tests__/{broker-snapshot-history.test.d.ts → serial/broker-snapshot-history.test.d.ts} +0 -0
- /package/dist/core/__tests__/{command-plugins.test.d.ts → serial/command-plugins.test.d.ts} +0 -0
- /package/dist/core/__tests__/{deferred-no-wake.test.d.ts → serial/deferred-no-wake.test.d.ts} +0 -0
- /package/dist/core/__tests__/{flagship-lifecycle.test.d.ts → serial/flagship-lifecycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{host-teardown-process-group.test.d.ts → serial/host-teardown-process-group.test.d.ts} +0 -0
- /package/dist/core/__tests__/{human-deliver-e2e.test.d.ts → serial/human-deliver-e2e.test.d.ts} +0 -0
- /package/dist/core/__tests__/{live-mutation.test.d.ts → serial/live-mutation.test.d.ts} +0 -0
- /package/dist/core/__tests__/{refresh-stall-recycle.test.d.ts → serial/refresh-stall-recycle.test.d.ts} +0 -0
- /package/dist/core/__tests__/{revive.test.d.ts → serial/revive.test.d.ts} +0 -0
- /package/dist/core/__tests__/{spawn-root.test.d.ts → serial/spawn-root.test.d.ts} +0 -0
- /package/dist/core/__tests__/{subscription-delivery.test.d.ts → serial/subscription-delivery.test.d.ts} +0 -0
- /package/dist/core/__tests__/{tmux-surface.test.d.ts → serial/tmux-surface.test.d.ts} +0 -0
- /package/dist/core/__tests__/{worktree.test.d.ts → serial/worktree.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/markdown-headings.test.d.ts → core/__tests__/watchdog-abort-arms-retry.test.d.ts} +0 -0
- /package/dist/{clients/attach/__tests__/session-identity-refresh.test.d.ts → core/broker-client/__tests__/transport-socket.test.d.ts} +0 -0
- /package/dist/core/human/__tests__/{inbox-core.test.d.ts → serial/inbox-core.test.d.ts} +0 -0
- /package/dist/{clients/inbox/review/__tests__/remap.test.d.ts → daemon/api/__tests__/node-model-validation.test.d.ts} +0 -0
- /package/dist/{clients/surfaces/region.js → daemon/api/__tests__/review-terminal-conflict.test.d.ts} +0 -0
- /package/dist/daemon/api/__tests__/{full → seam}/api-server.test.d.ts +0 -0
- /package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.d.ts +0 -0
- /package/dist/{clients/web/__tests__/push-engine.test.d.ts → daemon/api/__tests__/seam/leaf-api-parity.test.d.ts} +0 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
// tmux bindings — crouter-owned keybinding manifest persistence and reconciliation.
|
|
2
|
+
//
|
|
3
|
+
// Raw tmux process invocation stays in tmux-driver.ts. This module owns the
|
|
4
|
+
// persistent binding manifest, key parsing, and installation.
|
|
5
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { ATTACH_CONTROL_BINDINGS, BINDING_CATALOG, BINDING_IDS, encodeAttachControlInput, formatGesture, isAttachPaneBinding, resolveUserKeybindingSettings, } from '../keybindings/index.js';
|
|
9
|
+
import { normalizeTmuxPassthrough } from '../config.js';
|
|
10
|
+
import { withExclusiveDirectoryLock } from '../human/convention.js';
|
|
11
|
+
import { surfaceTmuxStyleArgs } from './surface-bg.js';
|
|
12
|
+
import { runTmux as tmux, shellQuote } from './tmux-driver.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Crouter-owned tmux bindings
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
const TMUX_BINDING_MANIFEST = '@crtr_keybindings_manifest';
|
|
17
|
+
const ROOT_TABLE = 'root';
|
|
18
|
+
const LEAF_MENU_ACTIONS = {
|
|
19
|
+
'crtr.tmux.menu.promote': { description: 'promote to orchestrator', action: { kind: 'keys', keys: '/promote' } },
|
|
20
|
+
'crtr.tmux.menu.resume': { description: 'find node', action: { kind: 'keys', keys: '/resume-node' } },
|
|
21
|
+
'crtr.tmux.menu.fork': { description: 'fork into idle root', action: { kind: 'direct', run: 'node fork --pane {pane}' } },
|
|
22
|
+
'crtr.tmux.menu.demote': { description: 'demote (finish when done)', action: { kind: 'command', run: 'node lifecycle demote --pane {pane}' } },
|
|
23
|
+
'crtr.tmux.menu.detach': { description: 'demote + close viewer', action: { kind: 'command', run: 'node lifecycle demote --pane {pane} --detach' } },
|
|
24
|
+
'crtr.tmux.menu.close-subtree': { description: 'close agent + subtree', action: { kind: 'command', run: 'node lifecycle close --pane {pane}' } },
|
|
25
|
+
'crtr.tmux.menu.background-bash': { description: 'background running bash', action: { kind: 'direct', run: 'node bash background --pane {pane}' } },
|
|
26
|
+
// M-S-r, not M-R: with extended-keys csi-u tmux encodes M-R as alt+uppercase-R
|
|
27
|
+
// WITHOUT the shift modifier bit, which pi-tui's `alt+shift+r` matcher rejects;
|
|
28
|
+
// M-S-r carries shift+alt and lands as the same chord the keyboard produces.
|
|
29
|
+
'crtr.tmux.menu.file-review': { description: 'review a file', action: { kind: 'raw-keys', keys: 'M-S-r' } },
|
|
30
|
+
'crtr.tmux.menu.metadata': { description: 'metadata', action: { kind: 'keys', keys: '/node-metadata' } },
|
|
31
|
+
'crtr.tmux.menu.fold-tools': { description: 'fold finished tool calls', action: { kind: 'keys', keys: '/fold-tools' } },
|
|
32
|
+
'crtr.tmux.menu.context': { description: 'context + reports', action: { kind: 'keys', keys: '/context' } },
|
|
33
|
+
// The settings page lays out a grouped rail beside a content panel; the
|
|
34
|
+
// default dialog-sized popup clipped both the rail and the models matrix.
|
|
35
|
+
'crtr.tmux.menu.providers': { description: 'settings', action: { kind: 'popup', run: 'sys settings', size: { w: '90%', h: '85%' } } },
|
|
36
|
+
'crtr.tmux.menu.graph': { description: 'canvas graph', action: { kind: 'keys', keys: '/graph' } },
|
|
37
|
+
'crtr.tmux.menu.focus-manager': { description: 'go to manager', action: { kind: 'command', run: 'surface node focus {manager}' } },
|
|
38
|
+
'crtr.tmux.menu.copy-id': { description: 'copy node id', action: { kind: 'command', run: 'surface node id --pane {pane}' } },
|
|
39
|
+
'crtr.tmux.menu.reload-viewer': { description: 'reload viewer', action: { kind: 'direct', run: 'surface reload --pane {pane}' } },
|
|
40
|
+
'crtr.tmux.menu.recap': { description: 'generate recap', action: { kind: 'keys', keys: '/recap' } },
|
|
41
|
+
'crtr.tmux.menu.pin': { description: 'pin conversation', action: { kind: 'direct', run: 'surface node pin --pane {pane}' } },
|
|
42
|
+
'crtr.tmux.menu.issue.idea': { description: 'idea', action: { kind: 'popup', run: 'canvas issue new --type idea' } },
|
|
43
|
+
'crtr.tmux.menu.issue.bug': { description: 'bug', action: { kind: 'popup', run: 'canvas issue new --type bug' } },
|
|
44
|
+
};
|
|
45
|
+
function escapeTmuxFormats(value) {
|
|
46
|
+
return value.replace(/#/g, '##');
|
|
47
|
+
}
|
|
48
|
+
function tmuxCommand(args) {
|
|
49
|
+
return args.map(shellQuote).join(' ');
|
|
50
|
+
}
|
|
51
|
+
/** Popups default to a small dialog. A surface that has to lay out its own
|
|
52
|
+
* navigation and a content panel side by side asks for the larger size. */
|
|
53
|
+
const DEFAULT_POPUP_SIZE = { w: '60%', h: '50%' };
|
|
54
|
+
function chordCommand(run, interactive = false, size = DEFAULT_POPUP_SIZE) {
|
|
55
|
+
const command = `crtr canvas chord --pane '#{pane_id}' --run ${shellQuote(escapeTmuxFormats(run))}${interactive ? ' --interactive' : ''}`;
|
|
56
|
+
return interactive
|
|
57
|
+
? {
|
|
58
|
+
argv: ['display-popup', '-E', '-w', size.w, '-h', size.h, '-d', '#{pane_current_path}', command],
|
|
59
|
+
menu: `display-popup -E -w ${size.w} -h ${size.h} -d "#{pane_current_path}" ${shellQuote(command)}`,
|
|
60
|
+
}
|
|
61
|
+
: {
|
|
62
|
+
argv: ['run-shell', `${command} >/dev/null 2>&1`],
|
|
63
|
+
menu: `run-shell ${shellQuote(`${command} >/dev/null 2>&1`)}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Dispatch an action straight to a `crtr` invocation, skipping the
|
|
67
|
+
* `canvas chord` hop entirely. Only sound for an action whose sole
|
|
68
|
+
* interpolation need is `{pane}` — tmux expands it natively as `#{pane_id}` at
|
|
69
|
+
* keypress time (a real tmux format, substituted AFTER escaping the static
|
|
70
|
+
* run text so the `#` here is never doubled), so chord's node-resolution
|
|
71
|
+
* round trip buys nothing. `-b` keeps dispatch non-blocking. */
|
|
72
|
+
function directCommand(run) {
|
|
73
|
+
const argv = escapeTmuxFormats(run).replace('{pane}', '#{pane_id}');
|
|
74
|
+
const command = `crtr ${argv} >/dev/null 2>&1`;
|
|
75
|
+
return {
|
|
76
|
+
argv: ['run-shell', '-b', command],
|
|
77
|
+
menu: `run-shell -b ${shellQuote(command)}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** One command constructor serves both display-menu selections and direct root
|
|
81
|
+
* shortcuts. Each result carries the argv bind-key needs plus the stable menu
|
|
82
|
+
* serialization existing Alt+C installs already use. */
|
|
83
|
+
function menuActionCommand(action) {
|
|
84
|
+
switch (action.kind) {
|
|
85
|
+
case 'disabled': return null;
|
|
86
|
+
case 'separator': return null;
|
|
87
|
+
// Choosing any menu item closes it; refresh-client is the deliberately
|
|
88
|
+
// inert command that lets the prefix shortcut choose this close row.
|
|
89
|
+
case 'dismiss': return { argv: ['refresh-client'], menu: 'refresh-client' };
|
|
90
|
+
case 'keys': {
|
|
91
|
+
const keys = escapeTmuxFormats(action.keys);
|
|
92
|
+
return {
|
|
93
|
+
// A root binding already runs in the pressed key's pane context. Its
|
|
94
|
+
// target must stay implicit: bind-key does not expand `#{pane_id}` in
|
|
95
|
+
// send-keys' target argument. A menu command is serialized later and
|
|
96
|
+
// does need the pane captured when the menu opened.
|
|
97
|
+
argv: ['send-keys', keys, 'Enter'],
|
|
98
|
+
menu: `send-keys -t '#{pane_id}' ${shellQuote(keys)} Enter`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
case 'raw-keys': {
|
|
102
|
+
const keys = escapeTmuxFormats(action.keys);
|
|
103
|
+
return {
|
|
104
|
+
argv: ['send-keys', keys],
|
|
105
|
+
menu: `send-keys -t '#{pane_id}' ${shellQuote(keys)}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
case 'literal-input': {
|
|
109
|
+
const input = escapeTmuxFormats(action.input);
|
|
110
|
+
return {
|
|
111
|
+
argv: ['send-keys', '-l', input],
|
|
112
|
+
menu: `send-keys -l -t '#{pane_id}' ${shellQuote(input)}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
case 'command': return chordCommand(action.run);
|
|
116
|
+
case 'direct': return directCommand(action.run);
|
|
117
|
+
case 'popup': return chordCommand(action.run, true, action.size);
|
|
118
|
+
case 'submenu': {
|
|
119
|
+
const argv = buildMenuArgs(action.title, action.items);
|
|
120
|
+
return { argv, menu: tmuxCommand(argv) };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function renderMenuAction(action) {
|
|
125
|
+
return menuActionCommand(action)?.menu ?? '';
|
|
126
|
+
}
|
|
127
|
+
/** Visible width of a menu item name: style markup (`#[...]`) takes no cells. */
|
|
128
|
+
function menuItemWidth(description) {
|
|
129
|
+
return description.replace(/#\[[^\]]*\]/g, '').length;
|
|
130
|
+
}
|
|
131
|
+
function buildMenuArgs(title, items) {
|
|
132
|
+
const safeTitle = escapeTmuxFormats(title);
|
|
133
|
+
const widestItem = items.reduce((width, item) => Math.max(width, menuItemWidth(item.description)), 0);
|
|
134
|
+
const args = ['display-menu', '-T', `#[align=centre]${safeTitle}`, ...surfaceTmuxStyleArgs()];
|
|
135
|
+
args.push('-x', `#{e|-:#{pane_right},${Math.max(safeTitle.length, widestItem) + 7}}`, '-y', '#{e|+:#{pane_top},3}', '--');
|
|
136
|
+
for (const item of items) {
|
|
137
|
+
// A separator is a single empty name with key and command omitted; tmux
|
|
138
|
+
// draws it as a horizontal rule. A header (disabled) name is a crouter-owned
|
|
139
|
+
// literal carrying `#[...]` style markup, so it skips format escaping.
|
|
140
|
+
if (item.action.kind === 'separator') {
|
|
141
|
+
args.push('');
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const name = item.action.kind === 'disabled' ? item.description : escapeTmuxFormats(item.description);
|
|
145
|
+
args.push(name, item.selector, renderMenuAction(item.action));
|
|
146
|
+
}
|
|
147
|
+
args.push('', '-esc / q to close', '', '');
|
|
148
|
+
return args;
|
|
149
|
+
}
|
|
150
|
+
const TMUX_NAMED_KEYS = {
|
|
151
|
+
enter: 'Enter', escape: 'Escape', tab: 'Tab', backspace: 'BSpace', delete: 'DC',
|
|
152
|
+
space: 'Space', up: 'Up', down: 'Down', left: 'Left', right: 'Right',
|
|
153
|
+
pageup: 'PPage', pagedown: 'NPage', home: 'Home', end: 'End',
|
|
154
|
+
};
|
|
155
|
+
function normalizedStrokeParts(stroke) {
|
|
156
|
+
if (stroke === '+')
|
|
157
|
+
return { key: '+', modifiers: [] };
|
|
158
|
+
if (stroke.endsWith('++'))
|
|
159
|
+
return { key: '+', modifiers: stroke.slice(0, -2).split('+') };
|
|
160
|
+
const parts = stroke.split('+');
|
|
161
|
+
return { key: parts.pop(), modifiers: parts };
|
|
162
|
+
}
|
|
163
|
+
function tmuxKey(stroke) {
|
|
164
|
+
let { key, modifiers: modifierParts } = normalizedStrokeParts(stroke);
|
|
165
|
+
const modifiers = new Set(modifierParts);
|
|
166
|
+
if (modifiers.has('shift') && key.length === 1 && /[a-z]/.test(key)) {
|
|
167
|
+
// tmux names a shifted printable with its uppercase character even when an
|
|
168
|
+
// extended-key terminal also preserves Shift as a modifier: Alt+Shift+F is
|
|
169
|
+
// `M-S-F`. Shift by itself needs only the uppercase character (`F`).
|
|
170
|
+
key = key.toUpperCase();
|
|
171
|
+
if (!modifiers.has('ctrl') && !modifiers.has('alt'))
|
|
172
|
+
modifiers.delete('shift');
|
|
173
|
+
}
|
|
174
|
+
key = TMUX_NAMED_KEYS[key] ?? key;
|
|
175
|
+
const prefix = [
|
|
176
|
+
modifiers.has('ctrl') ? 'C-' : '',
|
|
177
|
+
modifiers.has('alt') ? 'M-' : '',
|
|
178
|
+
modifiers.has('shift') ? 'S-' : '',
|
|
179
|
+
].join('');
|
|
180
|
+
return `${prefix}${key}`;
|
|
181
|
+
}
|
|
182
|
+
function menuSelector(stroke) {
|
|
183
|
+
const { key, modifiers } = normalizedStrokeParts(stroke);
|
|
184
|
+
return modifiers.includes('shift') && /[a-z]/.test(key) ? key.toUpperCase() : key === 'space' ? ' ' : key;
|
|
185
|
+
}
|
|
186
|
+
function boundMenuItems(ids, entries, bindings) {
|
|
187
|
+
const items = [];
|
|
188
|
+
for (const id of ids) {
|
|
189
|
+
const entry = entries.get(id);
|
|
190
|
+
if (entry === undefined)
|
|
191
|
+
continue;
|
|
192
|
+
for (const gesture of bindings.gestures(id)) {
|
|
193
|
+
items.push({ description: entry.description, selector: menuSelector(gesture), action: entry.action });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return items;
|
|
197
|
+
}
|
|
198
|
+
/** Resolve the complete action descriptor set once. Submenu openers point at
|
|
199
|
+
* the same child MenuItems the prefix menu renders, so their direct global
|
|
200
|
+
* shortcuts cannot drift from the menu path. */
|
|
201
|
+
function resolvedMenuActions(bindings) {
|
|
202
|
+
const entries = new Map();
|
|
203
|
+
for (const [id, entry] of Object.entries(LEAF_MENU_ACTIONS)) {
|
|
204
|
+
if (entry !== undefined)
|
|
205
|
+
entries.set(id, entry);
|
|
206
|
+
}
|
|
207
|
+
for (const { menuId, actionId } of ATTACH_CONTROL_BINDINGS) {
|
|
208
|
+
const definition = BINDING_CATALOG.find((candidate) => candidate.id === actionId);
|
|
209
|
+
if (definition === undefined)
|
|
210
|
+
throw new Error(`missing attach binding definition ${actionId}`);
|
|
211
|
+
entries.set(menuId, {
|
|
212
|
+
description: definition.name.toLowerCase(),
|
|
213
|
+
action: { kind: 'literal-input', input: encodeAttachControlInput(actionId) },
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const attachItems = boundMenuItems(ATTACH_CONTROL_BINDINGS.map(({ menuId }) => menuId), entries, bindings);
|
|
217
|
+
if (attachItems.length > 0) {
|
|
218
|
+
entries.set('crtr.tmux.menu.attach', {
|
|
219
|
+
description: 'attach viewer ›',
|
|
220
|
+
action: { kind: 'submenu', title: ' attach viewer ', items: attachItems },
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const showItems = boundMenuItems([
|
|
224
|
+
'crtr.tmux.menu.metadata',
|
|
225
|
+
'crtr.tmux.menu.context',
|
|
226
|
+
], entries, bindings);
|
|
227
|
+
entries.set('crtr.tmux.menu.show', {
|
|
228
|
+
description: 'inspect ›',
|
|
229
|
+
action: { kind: 'submenu', title: ' inspect node ', items: showItems },
|
|
230
|
+
});
|
|
231
|
+
// How this viewer PAINTS the transcript, as opposed to what it shows. Each item
|
|
232
|
+
// is a toggle the viewer applies to its own render state (a `/`-command sent to
|
|
233
|
+
// the pane); a tmux menu is built once and cannot show live state, so the item
|
|
234
|
+
// reads as the action and the viewer answers with a notice.
|
|
235
|
+
const viewSettingsItems = boundMenuItems(['crtr.tmux.menu.fold-tools'], entries, bindings);
|
|
236
|
+
entries.set('crtr.tmux.menu.view-settings', {
|
|
237
|
+
description: 'display ›',
|
|
238
|
+
action: { kind: 'submenu', title: ' display ', items: viewSettingsItems },
|
|
239
|
+
});
|
|
240
|
+
const issueItems = boundMenuItems([
|
|
241
|
+
'crtr.tmux.menu.issue.idea',
|
|
242
|
+
'crtr.tmux.menu.issue.bug',
|
|
243
|
+
], entries, bindings);
|
|
244
|
+
entries.set('crtr.tmux.menu.issues', {
|
|
245
|
+
description: 'create ›',
|
|
246
|
+
action: { kind: 'submenu', title: ' create ', items: issueItems },
|
|
247
|
+
});
|
|
248
|
+
return entries;
|
|
249
|
+
}
|
|
250
|
+
function menuItems(bindings, entries) {
|
|
251
|
+
const boundItems = (ids) => boundMenuItems(ids, entries, bindings);
|
|
252
|
+
const items = [];
|
|
253
|
+
const addGroup = (heading, groupItems) => {
|
|
254
|
+
if (groupItems.length === 0)
|
|
255
|
+
return;
|
|
256
|
+
if (items.length > 0)
|
|
257
|
+
items.push({ description: '', selector: '', action: { kind: 'separator' } });
|
|
258
|
+
items.push({ description: `-#[bold]${heading}`, selector: '', action: { kind: 'disabled' } }, ...groupItems);
|
|
259
|
+
};
|
|
260
|
+
// Explore changes what is in view or opens a canvas-level browser. Current
|
|
261
|
+
// node acts on the agent in this pane without changing its lifecycle.
|
|
262
|
+
addGroup('Explore', boundItems([
|
|
263
|
+
'crtr.tmux.menu.resume',
|
|
264
|
+
'crtr.tmux.menu.graph',
|
|
265
|
+
'crtr.tmux.menu.focus-manager',
|
|
266
|
+
'crtr.tmux.menu.show',
|
|
267
|
+
'crtr.tmux.menu.view-settings',
|
|
268
|
+
]));
|
|
269
|
+
addGroup('Current node', boundItems([
|
|
270
|
+
'crtr.tmux.menu.attach',
|
|
271
|
+
'crtr.tmux.menu.background-bash',
|
|
272
|
+
'crtr.tmux.menu.file-review',
|
|
273
|
+
'crtr.tmux.menu.pin',
|
|
274
|
+
'crtr.tmux.menu.copy-id',
|
|
275
|
+
'crtr.tmux.menu.reload-viewer',
|
|
276
|
+
'crtr.tmux.menu.recap',
|
|
277
|
+
]));
|
|
278
|
+
addGroup('Lifecycle', boundItems([
|
|
279
|
+
'crtr.tmux.menu.fork',
|
|
280
|
+
'crtr.tmux.menu.promote',
|
|
281
|
+
'crtr.tmux.menu.demote',
|
|
282
|
+
'crtr.tmux.menu.detach',
|
|
283
|
+
'crtr.tmux.menu.close-subtree',
|
|
284
|
+
]));
|
|
285
|
+
addGroup('System', boundItems([
|
|
286
|
+
'crtr.tmux.menu.providers',
|
|
287
|
+
'crtr.tmux.menu.issues',
|
|
288
|
+
]));
|
|
289
|
+
const prefixGestures = bindings.gestures('crtr.tmux.menu.open');
|
|
290
|
+
if (prefixGestures.length > 0 && items.length > 0) {
|
|
291
|
+
items.push({ description: '', selector: '', action: { kind: 'separator' } });
|
|
292
|
+
for (const gesture of prefixGestures) {
|
|
293
|
+
items.push({
|
|
294
|
+
description: `${formatGesture(gesture)} to close`,
|
|
295
|
+
// Menu selectors accept tmux key notation, including modifiers. This
|
|
296
|
+
// is intentionally the exact binding that opened the menu.
|
|
297
|
+
selector: tmuxKey(gesture),
|
|
298
|
+
action: { kind: 'dismiss' },
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return items;
|
|
303
|
+
}
|
|
304
|
+
/** Read one token from tmux's sourceable `list-keys` output. Key names that
|
|
305
|
+
* contain punctuation are quoted, so splitting on whitespace would make their
|
|
306
|
+
* displayed token differ from the key name accepted by bind/unbind commands. */
|
|
307
|
+
function readTmuxToken(line, from) {
|
|
308
|
+
let index = from;
|
|
309
|
+
while (index < line.length && /\s/.test(line[index]))
|
|
310
|
+
index += 1;
|
|
311
|
+
if (index >= line.length)
|
|
312
|
+
return null;
|
|
313
|
+
let quote = null;
|
|
314
|
+
let value = '';
|
|
315
|
+
while (index < line.length) {
|
|
316
|
+
const char = line[index];
|
|
317
|
+
if (quote === null && /\s/.test(char))
|
|
318
|
+
break;
|
|
319
|
+
if (quote === null && (char === "'" || char === '"')) {
|
|
320
|
+
quote = char;
|
|
321
|
+
index += 1;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (quote === char) {
|
|
325
|
+
quote = null;
|
|
326
|
+
index += 1;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (char === '\\' && quote !== "'" && index + 1 < line.length) {
|
|
330
|
+
value += line[index + 1];
|
|
331
|
+
index += 2;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
value += char;
|
|
335
|
+
index += 1;
|
|
336
|
+
}
|
|
337
|
+
return { value, end: index };
|
|
338
|
+
}
|
|
339
|
+
function parseTmuxBinding(line) {
|
|
340
|
+
const commandName = readTmuxToken(line, 0);
|
|
341
|
+
if (commandName?.value !== 'bind-key')
|
|
342
|
+
return null;
|
|
343
|
+
let cursor = commandName.end;
|
|
344
|
+
let repeat = false;
|
|
345
|
+
while (true) {
|
|
346
|
+
const token = readTmuxToken(line, cursor);
|
|
347
|
+
if (token === null)
|
|
348
|
+
return null;
|
|
349
|
+
cursor = token.end;
|
|
350
|
+
if (token.value === '-r') {
|
|
351
|
+
repeat = true;
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
if (token.value !== '-T')
|
|
355
|
+
continue;
|
|
356
|
+
const table = readTmuxToken(line, cursor);
|
|
357
|
+
if (table === null)
|
|
358
|
+
return null;
|
|
359
|
+
const key = readTmuxToken(line, table.end);
|
|
360
|
+
if (key === null)
|
|
361
|
+
return null;
|
|
362
|
+
return {
|
|
363
|
+
table: table.value,
|
|
364
|
+
key: key.value,
|
|
365
|
+
command: line.slice(key.end).trimStart(),
|
|
366
|
+
repeat,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function bindingAt(table, key) {
|
|
371
|
+
const result = tmux(['list-keys', '-T', table]);
|
|
372
|
+
if (!result.ok)
|
|
373
|
+
return null;
|
|
374
|
+
return result.stdout.split('\n').find((line) => {
|
|
375
|
+
const parsed = parseTmuxBinding(line);
|
|
376
|
+
return parsed?.table === table && parsed.key === key;
|
|
377
|
+
}) ?? null;
|
|
378
|
+
}
|
|
379
|
+
/** Effective crouter gestures that tmux's root table currently consumes before
|
|
380
|
+
* pane input can see them. One list-keys snapshot serves the whole query. */
|
|
381
|
+
export function occupiedTmuxRootGestures(gestures) {
|
|
382
|
+
const result = tmux(['list-keys', '-T', ROOT_TABLE]);
|
|
383
|
+
if (!result.ok)
|
|
384
|
+
return [];
|
|
385
|
+
const occupied = new Set(result.stdout.split('\n').flatMap((line) => {
|
|
386
|
+
const parsed = parseTmuxBinding(line);
|
|
387
|
+
return parsed?.table === ROOT_TABLE ? [parsed.key] : [];
|
|
388
|
+
}));
|
|
389
|
+
return gestures.filter((gesture) => !gesture.includes(' > ') && occupied.has(tmuxKey(gesture)));
|
|
390
|
+
}
|
|
391
|
+
function isInstalledPair(value) {
|
|
392
|
+
return typeof value === 'object' && value !== null
|
|
393
|
+
&& typeof value.table === 'string'
|
|
394
|
+
&& typeof value.key === 'string';
|
|
395
|
+
}
|
|
396
|
+
function isWrappedRootBinding(value) {
|
|
397
|
+
if (typeof value !== 'object' || value === null)
|
|
398
|
+
return false;
|
|
399
|
+
const wrapper = value;
|
|
400
|
+
return typeof wrapper.bindingId === 'string'
|
|
401
|
+
&& BINDING_IDS.has(wrapper.bindingId)
|
|
402
|
+
&& typeof wrapper.gesture === 'string'
|
|
403
|
+
&& typeof wrapper.key === 'string'
|
|
404
|
+
&& (typeof wrapper.original === 'string' || wrapper.original === null)
|
|
405
|
+
&& typeof wrapper.wrapper === 'string'
|
|
406
|
+
&& (wrapper.state === 'owned' || wrapper.state === 'relinquished');
|
|
407
|
+
}
|
|
408
|
+
function readManifest() {
|
|
409
|
+
const option = tmux(['show-options', '-s', '-q', TMUX_BINDING_MANIFEST]);
|
|
410
|
+
if (!option.ok)
|
|
411
|
+
return null;
|
|
412
|
+
if (option.stdout === '')
|
|
413
|
+
return { pairs: [], wrappers: [] };
|
|
414
|
+
const result = tmux(['show-options', '-s', '-qv', TMUX_BINDING_MANIFEST]);
|
|
415
|
+
if (!result.ok)
|
|
416
|
+
return null;
|
|
417
|
+
try {
|
|
418
|
+
const value = JSON.parse(result.stdout);
|
|
419
|
+
// Before passthrough wrappers, the manifest was the pair array itself.
|
|
420
|
+
if (Array.isArray(value))
|
|
421
|
+
return value.every(isInstalledPair) ? { pairs: value, wrappers: [] } : null;
|
|
422
|
+
if (typeof value !== 'object' || value === null)
|
|
423
|
+
return null;
|
|
424
|
+
const manifest = value;
|
|
425
|
+
if (!Array.isArray(manifest.pairs) || !manifest.pairs.every(isInstalledPair)
|
|
426
|
+
|| !Array.isArray(manifest.wrappers) || !manifest.wrappers.every(isWrappedRootBinding))
|
|
427
|
+
return null;
|
|
428
|
+
return { pairs: manifest.pairs, wrappers: manifest.wrappers };
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function writeManifest(manifest) {
|
|
435
|
+
return tmux(['set-option', '-s', TMUX_BINDING_MANIFEST, JSON.stringify(manifest)]);
|
|
436
|
+
}
|
|
437
|
+
/** Tmux parses semicolons once more even when argv already isolated them. */
|
|
438
|
+
function tmuxKeyArgument(key) {
|
|
439
|
+
return key.replace(/\\/g, '\\\\').replace(/;/g, '\\;');
|
|
440
|
+
}
|
|
441
|
+
function tmuxCommandToken(value) {
|
|
442
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/;/g, '\\;')}"`;
|
|
443
|
+
}
|
|
444
|
+
/** A top-level separator is printed as `\;` in a bind-key line, but the same
|
|
445
|
+
* sequence is literal after the command tail becomes one if-shell branch. */
|
|
446
|
+
function bindingCommandForBranch(command) {
|
|
447
|
+
let quote = null;
|
|
448
|
+
let out = '';
|
|
449
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
450
|
+
const char = command[index];
|
|
451
|
+
if (quote === null && (char === "'" || char === '"')) {
|
|
452
|
+
quote = char;
|
|
453
|
+
out += char;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (quote === char) {
|
|
457
|
+
quote = null;
|
|
458
|
+
out += char;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (char === '\\' && index + 1 < command.length) {
|
|
462
|
+
const next = command[index + 1];
|
|
463
|
+
if (quote === null && next === ';') {
|
|
464
|
+
out += ';';
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
out += char + next;
|
|
468
|
+
}
|
|
469
|
+
index += 1;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
out += char;
|
|
473
|
+
}
|
|
474
|
+
return out;
|
|
475
|
+
}
|
|
476
|
+
function sourceBindingLine(line) {
|
|
477
|
+
const dir = mkdtempSync(join(tmpdir(), 'crtr-tmux-binding-'));
|
|
478
|
+
const file = join(dir, 'binding.conf');
|
|
479
|
+
try {
|
|
480
|
+
writeFileSync(file, `${line}\n`, 'utf8');
|
|
481
|
+
return tmux(['source-file', file]);
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
rmSync(dir, { recursive: true, force: true });
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function restoreWrappedBinding(wrapper) {
|
|
488
|
+
return wrapper.original === null
|
|
489
|
+
? tmux(['unbind-key', '-T', ROOT_TABLE, tmuxKeyArgument(wrapper.key)])
|
|
490
|
+
: sourceBindingLine(wrapper.original);
|
|
491
|
+
}
|
|
492
|
+
// --quiet keeps the background `run-shell -b` binding from printing its result JSON, which
|
|
493
|
+
// tmux would otherwise surface as a view-mode overlay on the active pane. The popup opening
|
|
494
|
+
// is the feedback; a genuine failure still prints through the CLI toggle action. This deliberately
|
|
495
|
+
// invokes bare `crtr` from PATH: it is installed once and must resolve to the selected generation,
|
|
496
|
+
// not the sealed generation that installed it. The neighboring `crtr surface node cycle` builders
|
|
497
|
+
// share that property.
|
|
498
|
+
export function inboxToggleTmuxCommand() {
|
|
499
|
+
return ['run-shell', '-b', 'crtr human inbox toggle --quiet --tmux-socket "#{socket_path}" --tmux-client "#{client_name}" --target-pane "#{pane_id}"'];
|
|
500
|
+
}
|
|
501
|
+
function actionCommand(id) {
|
|
502
|
+
switch (id) {
|
|
503
|
+
case 'crtr.tmux.node.next': return ['run-shell', `crtr surface node cycle --dir next --pane '#{pane_id}' >/dev/null 2>&1`];
|
|
504
|
+
case 'crtr.tmux.node.previous': return ['run-shell', `crtr surface node cycle --dir prev --pane '#{pane_id}' >/dev/null 2>&1`];
|
|
505
|
+
case 'crtr.tmux.inbox.toggle': return inboxToggleTmuxCommand();
|
|
506
|
+
default: throw new Error(`no tmux command for ${id}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/** A directory beside the server socket serializes every process driving that
|
|
510
|
+
* exact tmux server. Outside tmux, ask the default server for its socket first;
|
|
511
|
+
* an unavailable server uses one per-user fallback lock for the error path. */
|
|
512
|
+
function tmuxBindingLockPath() {
|
|
513
|
+
const envSocket = process.env['TMUX']?.split(',')[0]?.trim();
|
|
514
|
+
if (envSocket !== undefined && envSocket !== '')
|
|
515
|
+
return `${envSocket}.crtr-keybindings.lock`;
|
|
516
|
+
const result = tmux(['display-message', '-p', '#{socket_path}']);
|
|
517
|
+
if (result.ok && result.stdout !== '')
|
|
518
|
+
return `${result.stdout}.crtr-keybindings.lock`;
|
|
519
|
+
const user = typeof process.getuid === 'function' ? process.getuid() : 'user';
|
|
520
|
+
return join(tmpdir(), `crtr-tmux-keybindings-${user}.lock`);
|
|
521
|
+
}
|
|
522
|
+
/** Reconcile the tmux server with one immutable crouter settings snapshot.
|
|
523
|
+
* Passing snapshots is hermetic; the no-argument front door reads both user
|
|
524
|
+
* settings from the same config read. The server lock covers the entire
|
|
525
|
+
* manifest read, restore, install, fingerprint, and write transition. */
|
|
526
|
+
export function installTmuxBindings(bindingSnapshot, passthroughSnapshot = []) {
|
|
527
|
+
return withExclusiveDirectoryLock(tmuxBindingLockPath(), () => reconcileTmuxBindings(bindingSnapshot, passthroughSnapshot));
|
|
528
|
+
}
|
|
529
|
+
function reconcileTmuxBindings(bindingSnapshot, passthroughSnapshot = []) {
|
|
530
|
+
const userSettings = bindingSnapshot === undefined ? resolveUserKeybindingSettings() : undefined;
|
|
531
|
+
const bindings = bindingSnapshot ?? userSettings.bindings;
|
|
532
|
+
const passthrough = bindingSnapshot === undefined
|
|
533
|
+
? userSettings.tmuxPassthrough
|
|
534
|
+
: normalizeTmuxPassthrough(passthroughSnapshot);
|
|
535
|
+
const diagnostics = [];
|
|
536
|
+
const installed = [];
|
|
537
|
+
const manifest = readManifest();
|
|
538
|
+
if (manifest === null) {
|
|
539
|
+
diagnostics.push({ code: 'tmux-error', message: `tmux is unavailable or ${TMUX_BINDING_MANIFEST} is malformed` });
|
|
540
|
+
return { ok: false, installed, diagnostics };
|
|
541
|
+
}
|
|
542
|
+
const desiredWrappers = new Map();
|
|
543
|
+
for (const bindingId of passthrough) {
|
|
544
|
+
const definition = BINDING_CATALOG.find((candidate) => candidate.id === bindingId);
|
|
545
|
+
if (definition === undefined || !isAttachPaneBinding(definition))
|
|
546
|
+
continue;
|
|
547
|
+
for (const gesture of bindings.gestures(bindingId)) {
|
|
548
|
+
if (gesture.includes(' > '))
|
|
549
|
+
continue;
|
|
550
|
+
const key = tmuxKey(gesture);
|
|
551
|
+
if (!desiredWrappers.has(key))
|
|
552
|
+
desiredWrappers.set(key, { bindingId, gesture });
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
// Restore every wrapper crouter still owns before the ordinary binding sweep.
|
|
556
|
+
// A live line that differs from the fingerprint is a user's later rebind: it
|
|
557
|
+
// is protected from this and every later reconciliation until they toggle the
|
|
558
|
+
// passthrough preference off and on again.
|
|
559
|
+
const protectedRootKeys = new Set();
|
|
560
|
+
const wrappers = [];
|
|
561
|
+
for (const prior of manifest.wrappers) {
|
|
562
|
+
const desired = desiredWrappers.get(prior.key);
|
|
563
|
+
const live = bindingAt(ROOT_TABLE, prior.key);
|
|
564
|
+
if (prior.state === 'relinquished' || live !== prior.wrapper) {
|
|
565
|
+
protectedRootKeys.add(prior.key);
|
|
566
|
+
if (desired !== undefined) {
|
|
567
|
+
wrappers.push({ ...prior, ...desired, state: 'relinquished' });
|
|
568
|
+
}
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
const restored = restoreWrappedBinding(prior);
|
|
572
|
+
if (!restored.ok) {
|
|
573
|
+
protectedRootKeys.add(prior.key);
|
|
574
|
+
wrappers.push(prior);
|
|
575
|
+
diagnostics.push({
|
|
576
|
+
code: 'tmux-error',
|
|
577
|
+
bindingId: prior.bindingId,
|
|
578
|
+
gesture: prior.gesture,
|
|
579
|
+
message: `could not restore tmux binding ${ROOT_TABLE}:${prior.key}: ${restored.stderr}`,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
for (const pair of manifest.pairs) {
|
|
584
|
+
if (pair.table === ROOT_TABLE && protectedRootKeys.has(pair.key))
|
|
585
|
+
continue;
|
|
586
|
+
const result = tmux(['unbind-key', '-T', pair.table, tmuxKeyArgument(pair.key)]);
|
|
587
|
+
if (!result.ok && bindingAt(pair.table, pair.key) !== null) {
|
|
588
|
+
diagnostics.push({ code: 'tmux-error', message: `could not remove crouter binding ${pair.table}:${pair.key}: ${result.stderr}` });
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (diagnostics.some((diagnostic) => diagnostic.code === 'tmux-error')) {
|
|
592
|
+
const save = writeManifest({ pairs: manifest.pairs, wrappers });
|
|
593
|
+
if (!save.ok)
|
|
594
|
+
diagnostics.push({ code: 'tmux-error', message: `could not save crouter tmux ownership manifest: ${save.stderr}` });
|
|
595
|
+
return { ok: false, installed, diagnostics };
|
|
596
|
+
}
|
|
597
|
+
const install = (table, key, command, id, gesture) => {
|
|
598
|
+
if (bindingAt(table, key) !== null) {
|
|
599
|
+
diagnostics.push({ code: 'occupied', bindingId: id, gesture, message: `${table}:${key} is already bound by tmux; ${id} was not installed` });
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
const result = tmux(['bind-key', '-T', table, tmuxKeyArgument(key), ...command]);
|
|
603
|
+
if (!result.ok) {
|
|
604
|
+
diagnostics.push({ code: 'tmux-error', bindingId: id, gesture, message: `could not install ${id}: ${result.stderr}` });
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
installed.push({ table, key });
|
|
608
|
+
return true;
|
|
609
|
+
};
|
|
610
|
+
const menuActions = resolvedMenuActions(bindings);
|
|
611
|
+
const menuCommand = buildMenuArgs(' crtr ', menuItems(bindings, menuActions));
|
|
612
|
+
for (const gesture of bindings.gestures('crtr.tmux.menu.open')) {
|
|
613
|
+
install(ROOT_TABLE, tmuxKey(gesture), menuCommand, 'crtr.tmux.menu.open', gesture);
|
|
614
|
+
}
|
|
615
|
+
for (const definition of BINDING_CATALOG) {
|
|
616
|
+
if (definition.pair?.field !== 'menu')
|
|
617
|
+
continue;
|
|
618
|
+
const paired = BINDING_CATALOG.find((candidate) => candidate.id === definition.pair?.pairedId);
|
|
619
|
+
if (paired?.pair?.field !== 'global')
|
|
620
|
+
continue;
|
|
621
|
+
const menuId = definition.id;
|
|
622
|
+
const globalId = paired.id;
|
|
623
|
+
const entry = menuActions.get(menuId);
|
|
624
|
+
const command = entry === undefined ? null : menuActionCommand(entry.action);
|
|
625
|
+
if (command === null)
|
|
626
|
+
throw new Error(`no direct tmux action for paired menu binding ${menuId}`);
|
|
627
|
+
for (const gesture of bindings.gestures(globalId)) {
|
|
628
|
+
install(ROOT_TABLE, tmuxKey(gesture), command.argv, globalId, gesture);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
for (const id of ['crtr.tmux.node.next', 'crtr.tmux.node.previous', 'crtr.tmux.inbox.toggle']) {
|
|
632
|
+
for (const gesture of bindings.gestures(id))
|
|
633
|
+
install(ROOT_TABLE, tmuxKey(gesture), actionCommand(id), id, gesture);
|
|
634
|
+
}
|
|
635
|
+
// Wrap only occupied roots. Unoccupied gestures already reach the pane and do
|
|
636
|
+
// not need crouter ownership. Re-read the installed line as the fingerprint;
|
|
637
|
+
// tmux canonicalizes quoting and braces in ways hand serialization cannot.
|
|
638
|
+
const newlyWrapped = [];
|
|
639
|
+
for (const [key, desired] of desiredWrappers) {
|
|
640
|
+
if (protectedRootKeys.has(key))
|
|
641
|
+
continue;
|
|
642
|
+
const original = bindingAt(ROOT_TABLE, key);
|
|
643
|
+
if (original === null)
|
|
644
|
+
continue;
|
|
645
|
+
const parsed = parseTmuxBinding(original);
|
|
646
|
+
if (parsed === null || parsed.command === '') {
|
|
647
|
+
diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not parse occupied tmux binding ${ROOT_TABLE}:${key}` });
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
const result = tmux([
|
|
651
|
+
'bind-key',
|
|
652
|
+
...(parsed.repeat ? ['-r'] : []),
|
|
653
|
+
'-T', ROOT_TABLE, tmuxKeyArgument(key),
|
|
654
|
+
'if-shell', '-F', '#{!=:#{@crtr_attach},}',
|
|
655
|
+
`send-keys ${tmuxCommandToken(key)}`,
|
|
656
|
+
bindingCommandForBranch(parsed.command),
|
|
657
|
+
]);
|
|
658
|
+
if (!result.ok) {
|
|
659
|
+
diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not wrap ${ROOT_TABLE}:${key}: ${result.stderr}` });
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
const wrapper = bindingAt(ROOT_TABLE, key);
|
|
663
|
+
if (wrapper === null || !wrapper.includes('@crtr_attach')) {
|
|
664
|
+
diagnostics.push({ code: 'tmux-error', bindingId: desired.bindingId, gesture: desired.gesture, message: `could not fingerprint wrapped tmux binding ${ROOT_TABLE}:${key}` });
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
const record = { ...desired, key, original, wrapper, state: 'owned' };
|
|
668
|
+
wrappers.push(record);
|
|
669
|
+
newlyWrapped.push(record);
|
|
670
|
+
}
|
|
671
|
+
const save = writeManifest({ pairs: installed, wrappers });
|
|
672
|
+
if (!save.ok) {
|
|
673
|
+
diagnostics.push({ code: 'tmux-error', message: `could not save crouter tmux ownership manifest: ${save.stderr}` });
|
|
674
|
+
// A wrapper without restoration data is unsafe. Put each newly wrapped key
|
|
675
|
+
// back immediately when manifest persistence fails.
|
|
676
|
+
for (const wrapper of newlyWrapped) {
|
|
677
|
+
if (bindingAt(ROOT_TABLE, wrapper.key) === wrapper.wrapper)
|
|
678
|
+
restoreWrappedBinding(wrapper);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
return { ok: diagnostics.every((diagnostic) => diagnostic.code !== 'tmux-error'), installed, diagnostics };
|
|
682
|
+
}
|