tmux-ide 2.9.2 → 2.9.4
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/LICENSE +1 -1
- package/README.md +121 -140
- package/bin/cli.js +2457 -727
- package/package.json +23 -8
- package/packages/contracts/src/desktop-host.ts +3 -0
- package/packages/contracts/src/saved-machines.ts +19 -0
- package/packages/contracts/src/terminal-replica.ts +1 -1
- package/packages/core/package.json +3 -2
- package/packages/core/src/terminal-delivery.ts +51 -3
- package/packages/core/src/terminal-fnv64-wasm.ts +4 -3
- package/packages/core/src/terminal-replica-hash-cache.ts +129 -31
- package/packages/core/src/terminal-replica-owned-row.ts +27 -0
- package/packages/core/src/terminal-replica.ts +13 -28
- package/packages/daemon/dist/command-center/daemon-fleet-facts-observer.js +96 -10
- package/packages/daemon/dist/command-center/resources/saved-machine-route.js +40 -1
- package/packages/daemon/dist/command-center/ws-events.js +5 -24
- package/packages/daemon/dist/lib/canonical-daemon-bootstrap.js +8 -0
- package/packages/daemon/dist/lib/canonical-daemon.js +28 -2
- package/packages/daemon/dist/lib/daemon-embed.js +1 -1
- package/packages/daemon/dist/lib/daemon-service-manager.js +122 -0
- package/packages/daemon/dist/lib/daemon-service-plan.js +127 -0
- package/packages/daemon/dist/lib/daemon-service.js +259 -0
- package/packages/daemon/dist/lib/install-origin.js +2 -0
- package/packages/daemon/dist/lib/local-fleet-request.js +9 -3
- package/packages/daemon/dist/lib/log.js +6 -10
- package/packages/daemon/dist/lib/managed-installer-update.js +67 -0
- package/packages/daemon/dist/lib/{tui-download-lock.js → private-operation-lock.js} +11 -11
- package/packages/daemon/dist/lib/remote-daemon-info.js +3 -2
- package/packages/daemon/dist/lib/remote-daemon-lifecycle.js +4 -4
- package/packages/daemon/dist/lib/remote-tmux-command.js +10 -0
- package/packages/daemon/dist/lib/runtime-namespace.js +28 -16
- package/packages/daemon/dist/lib/saved-machines.js +40 -2
- package/packages/daemon/dist/lib/ssh-daemon-transport.js +24 -9
- package/packages/daemon/dist/lib/tmux-server-owner.js +1 -1
- package/packages/daemon/dist/lib/tmux-server-registration.js +21 -3
- package/packages/daemon/dist/lib/tui-binary.js +2 -2
- package/packages/daemon/dist/lib/update.js +18 -0
- package/packages/daemon/dist/lib/workspace-multiplexer-verbs.js +24 -1
- package/packages/daemon/dist/machines.js +40 -4
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
- package/packages/daemon/dist/terminal/attachments/direct-websocket.js +4 -1
- package/packages/daemon/dist/terminal/attachments/native-runtime.js +22 -13
- package/packages/daemon/dist/terminal/mirror/mirror-service.js +101 -3
- package/packages/daemon/dist/terminal/mirror/registered-window-guard.js +62 -0
- package/packages/daemon/dist/terminal/mirror/session-channel.js +75 -4
- package/packages/daemon/dist/terminal/mirror/shared-window-index.js +45 -0
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +11 -1
- package/packages/daemon/dist/terminal/session-runtime/multiplexer-backend.js +1 -0
- package/packages/daemon/dist/terminal/session-runtime/registry.js +56 -7
- package/packages/daemon/dist/terminal/session-runtime/semantic-mutation-executor.js +3 -1
- package/packages/daemon/dist/tui/integrations/claude-agent-teams.js +264 -0
- package/packages/daemon/dist/tui/mirror/dialog-model.js +2 -35
- package/packages/daemon/dist/tui/mirror/runtime/application-daemon-authority-owner.js +8 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-home-fleet.js +20 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-agents.js +7 -1
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-authority.js +59 -9
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-navigation.js +27 -19
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +23 -18
- package/packages/daemon/dist/tui/mirror/runtime/application-machine-startup.js +31 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-palette-search-owner.js +7 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-performance-log.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-input-ingress.js +146 -2
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +201 -50
- package/packages/daemon/dist/tui/mirror/runtime/bounded-performance-record-writer.js +50 -10
- package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -5
- package/packages/daemon/dist/tui/mirror/runtime/terminal-pane-input-router.js +3 -0
- package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +8 -2
- package/packages/daemon/dist/tui/mirror/terminal-text.js +35 -0
- package/packages/daemon/dist/tui/mirror/workspace/connection-feedback.js +5 -3
- package/packages/daemon/src/command-center/daemon-fleet-facts-observer.ts +100 -10
- package/packages/daemon/src/command-center/resources/saved-machine-route.ts +48 -1
- package/packages/daemon/src/command-center/ws-events.ts +5 -25
- package/packages/daemon/src/lib/canonical-daemon-bootstrap.ts +12 -0
- package/packages/daemon/src/lib/canonical-daemon.ts +44 -2
- package/packages/daemon/src/lib/daemon-embed.ts +1 -0
- package/packages/daemon/src/lib/daemon-service-manager.ts +152 -0
- package/packages/daemon/src/lib/daemon-service-plan.ts +153 -0
- package/packages/daemon/src/lib/daemon-service.ts +301 -0
- package/packages/daemon/src/lib/install-origin.ts +5 -0
- package/packages/daemon/src/lib/local-fleet-request.ts +19 -3
- package/packages/daemon/src/lib/log.ts +13 -9
- package/packages/daemon/src/lib/managed-installer-update.ts +79 -0
- package/packages/daemon/src/lib/{tui-download-lock.ts → private-operation-lock.ts} +15 -11
- package/packages/daemon/src/lib/remote-daemon-info.ts +3 -3
- package/packages/daemon/src/lib/remote-daemon-lifecycle.ts +4 -4
- package/packages/daemon/src/lib/remote-tmux-command.ts +12 -0
- package/packages/daemon/src/lib/runtime-namespace.ts +27 -18
- package/packages/daemon/src/lib/saved-machines.ts +40 -1
- package/packages/daemon/src/lib/ssh-daemon-transport.ts +25 -7
- package/packages/daemon/src/lib/tmux-server-owner.ts +1 -0
- package/packages/daemon/src/lib/tmux-server-registration.ts +21 -3
- package/packages/daemon/src/lib/tui-binary.ts +2 -2
- package/packages/daemon/src/lib/update.ts +24 -0
- package/packages/daemon/src/lib/workspace-multiplexer-verbs.ts +22 -0
- package/packages/daemon/src/machines.ts +53 -5
- package/packages/daemon/src/terminal/attachments/direct-websocket.ts +8 -2
- package/packages/daemon/src/terminal/attachments/native-runtime.ts +28 -13
- package/packages/daemon/src/terminal/mirror/mirror-service.ts +105 -3
- package/packages/daemon/src/terminal/mirror/registered-window-guard.ts +66 -0
- package/packages/daemon/src/terminal/mirror/session-channel.ts +92 -4
- package/packages/daemon/src/terminal/mirror/shared-window-index.ts +42 -0
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +12 -1
- package/packages/daemon/src/terminal/session-runtime/multiplexer-backend.ts +2 -0
- package/packages/daemon/src/terminal/session-runtime/registry.ts +70 -8
- package/packages/daemon/src/terminal/session-runtime/semantic-mutation-executor.ts +3 -1
- package/packages/daemon/src/tui/integrations/claude-agent-teams.ts +334 -0
- package/packages/daemon/src/tui/mirror/dialog-model.ts +2 -32
- package/packages/daemon/src/tui/mirror/runtime/application-daemon-authority-owner.ts +8 -0
- package/packages/daemon/src/tui/mirror/runtime/application-home-fleet.ts +19 -2
- package/packages/daemon/src/tui/mirror/runtime/application-machine-agents.ts +6 -1
- package/packages/daemon/src/tui/mirror/runtime/application-machine-authority.ts +60 -11
- package/packages/daemon/src/tui/mirror/runtime/application-machine-navigation.ts +39 -33
- package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +31 -23
- package/packages/daemon/src/tui/mirror/runtime/application-machine-startup.ts +38 -2
- package/packages/daemon/src/tui/mirror/runtime/application-palette-search-owner.ts +7 -4
- package/packages/daemon/src/tui/mirror/runtime/application-performance-log.ts +3 -0
- package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +1 -1
- package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +47 -46
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-input-ingress.ts +165 -2
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +207 -53
- package/packages/daemon/src/tui/mirror/runtime/bounded-performance-record-writer.ts +49 -9
- package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +6 -5
- package/packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.ts +4 -0
- package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +8 -3
- package/packages/daemon/src/tui/mirror/terminal-text.ts +32 -0
- package/packages/daemon/src/tui/mirror/workspace/connection-feedback.ts +6 -3
- package/packages/daemon-client/src/fleet-connection-status.ts +10 -1
- package/packages/daemon-client/src/workspace-client.ts +4 -1
- package/skill/SKILL.md +30 -30
- package/packages/contracts/src/__tests__/actions-contract.test.ts +0 -119
- package/packages/contracts/src/__tests__/agent-graph-overlay.test.ts +0 -428
- package/packages/contracts/src/__tests__/app-window-mutation.test.ts +0 -51
- package/packages/contracts/src/__tests__/app-window-state.test.ts +0 -34
- package/packages/contracts/src/__tests__/application-shell.test.ts +0 -645
- package/packages/contracts/src/__tests__/automation-operations.test.ts +0 -78
- package/packages/contracts/src/__tests__/causal-cell.test.ts +0 -47
- package/packages/contracts/src/__tests__/client-view-state.test.ts +0 -115
- package/packages/contracts/src/__tests__/cohesion-fixture.test.ts +0 -106
- package/packages/contracts/src/__tests__/commands.test.ts +0 -86
- package/packages/contracts/src/__tests__/control.test.ts +0 -154
- package/packages/contracts/src/__tests__/daemon-events.test.ts +0 -405
- package/packages/contracts/src/__tests__/daemon-resources.test.ts +0 -143
- package/packages/contracts/src/__tests__/daemon-wire.test.ts +0 -149
- package/packages/contracts/src/__tests__/desktop-host.test.ts +0 -269
- package/packages/contracts/src/__tests__/desktop-missions.test.ts +0 -215
- package/packages/contracts/src/__tests__/experience-conformance.test.ts +0 -153
- package/packages/contracts/src/__tests__/experience-kernel-boundary.test.ts +0 -113
- package/packages/contracts/src/__tests__/experience-shell.test.ts +0 -65
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v2.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v3.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/daemon-wire-v4.json +0 -35
- package/packages/contracts/src/__tests__/fixtures/scoped-receipt.ts +0 -62
- package/packages/contracts/src/__tests__/fleet-agent-graph.test.ts +0 -231
- package/packages/contracts/src/__tests__/fleet-catalog.test.ts +0 -270
- package/packages/contracts/src/__tests__/fleet-lifecycle.test.ts +0 -62
- package/packages/contracts/src/__tests__/focus-overlay.test.ts +0 -110
- package/packages/contracts/src/__tests__/ide-config.test.ts +0 -64
- package/packages/contracts/src/__tests__/interaction-evidence.test.ts +0 -288
- package/packages/contracts/src/__tests__/interaction-receipt-v2.test.ts +0 -72
- package/packages/contracts/src/__tests__/issue-error.test.ts +0 -78
- package/packages/contracts/src/__tests__/mission-projections.test.ts +0 -186
- package/packages/contracts/src/__tests__/missions.test.ts +0 -272
- package/packages/contracts/src/__tests__/multiplexer-verbs.test.ts +0 -242
- package/packages/contracts/src/__tests__/pane-appearance.test.ts +0 -146
- package/packages/contracts/src/__tests__/pane-read-result.test.ts +0 -55
- package/packages/contracts/src/__tests__/pane-stream.test.ts +0 -536
- package/packages/contracts/src/__tests__/pane-widget-marker.test.ts +0 -327
- package/packages/contracts/src/__tests__/performance-metrics.test.ts +0 -73
- package/packages/contracts/src/__tests__/performance-qualification.test.ts +0 -137
- package/packages/contracts/src/__tests__/rich-card-widget.test.ts +0 -29
- package/packages/contracts/src/__tests__/session-runtime.test.ts +0 -512
- package/packages/contracts/src/__tests__/startup-readiness.test.ts +0 -409
- package/packages/contracts/src/__tests__/terminal-attachment-stream.test.ts +0 -72
- package/packages/contracts/src/__tests__/terminal-attachments.test.ts +0 -329
- package/packages/contracts/src/__tests__/terminal-delivery.test.ts +0 -88
- package/packages/contracts/src/__tests__/terminal-runtime-inventory.test.ts +0 -68
- package/packages/contracts/src/__tests__/terminals.test.ts +0 -87
- package/packages/contracts/src/__tests__/tmux-server-scope.test.ts +0 -166
- package/packages/contracts/src/__tests__/transport-contract-loading.test.ts +0 -91
- package/packages/contracts/src/__tests__/visual-tokens.test.ts +0 -224
- package/packages/contracts/src/__tests__/window-links.test.ts +0 -243
- package/packages/contracts/src/__tests__/workspace-changes-resource.test.ts +0 -317
- package/packages/contracts/src/__tests__/workspace-changes-view.test.ts +0 -122
- package/packages/contracts/src/__tests__/workspace-config.test.ts +0 -294
- package/packages/contracts/src/__tests__/workspace-files-resource.test.ts +0 -261
- package/packages/contracts/src/__tests__/workspace-files-tree.test.ts +0 -147
- package/packages/contracts/src/__tests__/workspace-pane-creation.test.ts +0 -249
- package/packages/contracts/src/__tests__/workspace-promotion.test.ts +0 -94
- package/packages/contracts/src/__tests__/workspace-resource-identity.test.ts +0 -90
- package/packages/contracts/src/daemon-resource-request.test.ts +0 -192
- package/packages/contracts/src/native-operation-identity.test.ts +0 -23
- package/packages/contracts/src/workspace-admission.test.ts +0 -21
- package/packages/contracts/src/workspace-catalog-resource.test.ts +0 -146
- package/packages/core/src/agent-provisioning.test.ts +0 -42
- package/packages/core/src/application-shell-session.test.ts +0 -105
- package/packages/core/src/interaction-evidence.test.ts +0 -134
- package/packages/core/src/interaction-flow.test.ts +0 -66
- package/packages/core/src/interaction-receipts.test.ts +0 -671
- package/packages/core/src/navigator.test.ts +0 -34
- package/packages/core/src/optimistic-projection-conformance.test.ts +0 -75
- package/packages/core/src/optimistic-projection.test.ts +0 -118
- package/packages/core/src/performance-metrics.test.ts +0 -121
- package/packages/core/src/performance-qualification.test.ts +0 -283
- package/packages/core/src/saved-machines.test.ts +0 -80
- package/packages/core/src/terminal-compact-cold.test.ts +0 -87
- package/packages/core/src/terminal-conformance.test.ts +0 -48
- package/packages/core/src/terminal-delivery-cache-lifetime.test.ts +0 -167
- package/packages/core/src/terminal-delivery-encode-cooperative.test.ts +0 -140
- package/packages/core/src/terminal-delivery-representation-hash.test.ts +0 -77
- package/packages/core/src/terminal-delivery.test.ts +0 -1471
- package/packages/core/src/terminal-fnv64-wasm.test.ts +0 -61
- package/packages/core/src/terminal-replica-cold.test.ts +0 -31
- package/packages/core/src/terminal-replica-cooperative.test.ts +0 -170
- package/packages/core/src/terminal-replica-hash-cache.test.ts +0 -283
- package/packages/core/src/terminal-replica.test.ts +0 -347
- package/packages/core/src/workspace-selection.test.ts +0 -63
- package/packages/daemon/dist/__tests__/support.js +0 -33
- package/packages/daemon/dist/lib/__tests__/fixtures/workspace-state-writer.js +0 -58
- package/packages/daemon/dist/lib/__tests__/installed-recovery-fixture.js +0 -400
- package/packages/daemon/dist/terminal/__tests__/MockPtyAdapter.js +0 -182
- package/packages/daemon/dist/terminal/mirror/__tests__/scripted-channel.js +0 -98
- package/packages/daemon/dist/terminal/mirror/__tests__/simulated-channel.js +0 -156
- package/packages/daemon/src/tui/mirror/__snapshots__/activity-surface-renderer.test.tsx.snap +0 -42
- package/packages/daemon/src/tui/mirror/__snapshots__/changes-terminal-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/home-files-surface-renderer.test.tsx.snap +0 -267
- package/packages/daemon/src/tui/mirror/__snapshots__/missions-surface-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/__snapshots__/recipes-gallery-renderer.test.tsx.snap +0 -141
- package/packages/daemon/src/tui/mirror/__snapshots__/shell-chrome-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/runtime/__snapshots__/application-shell-view-renderer.test.tsx.snap +0 -87
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/agent-terminal-canvas-renderer.test.tsx.snap +0 -348
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/application-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/command-palette-surface-renderer.test.tsx.snap +0 -255
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/pane-frame-renderer.test.tsx.snap +0 -525
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/terminal-pane-header-polish-renderer.test.tsx.snap +0 -31
- package/packages/daemon/src/tui/mirror/workspace/__snapshots__/workbench-shell-renderer.test.tsx.snap +0 -134
- package/packages/daemon-client/src/application-shell-session.test.ts +0 -196
- package/packages/daemon-client/src/automation-client.test.ts +0 -214
- package/packages/daemon-client/src/bootstrap-coordinator.test.ts +0 -145
- package/packages/daemon-client/src/connection-supervisor.test.ts +0 -109
- package/packages/daemon-client/src/direct-application-shell-transport.test.ts +0 -941
- package/packages/daemon-client/src/first-latest-coordinator.test.ts +0 -175
- package/packages/daemon-client/src/fleet-dial-scheduler.test.ts +0 -114
- package/packages/daemon-client/src/fleet-environments.test.ts +0 -40
- package/packages/daemon-client/src/generation-bound-store-timers.test.ts +0 -74
- package/packages/daemon-client/src/owner-action-client.test.ts +0 -245
- package/packages/daemon-client/src/pane-stream-client.test.ts +0 -2841
- package/packages/daemon-client/src/pane-stream-clock-calibration.test.ts +0 -207
- package/packages/daemon-client/src/pane-stream-loopback-benchmark.test.ts +0 -51
- package/packages/daemon-client/src/push-resource-session.test.ts +0 -509
- package/packages/daemon-client/src/resource-replica.test.ts +0 -186
- package/packages/daemon-client/src/runtime-resource-ledger.test.ts +0 -18
- package/packages/daemon-client/src/scoped-tmux-server-transport.test.ts +0 -114
- package/packages/daemon-client/src/terminal-fast-lane.test.ts +0 -835
- package/packages/daemon-client/src/tmux-server-client.test.ts +0 -279
- package/packages/daemon-client/src/tmux-server-interaction-events.test.ts +0 -230
- package/packages/daemon-client/src/workspace-catalog-v2.test.ts +0 -109
- package/packages/daemon-client/src/workspace-client-boundaries.test.ts +0 -59
- package/packages/daemon-client/src/workspace-client-operations.test.ts +0 -96
- package/packages/daemon-client/src/workspace-client.test.ts +0 -2912
- package/packages/daemon-client/src/workspace-event-supervisor.test.ts +0 -1086
- package/packages/daemon-client/src/workspace-pane-client.test.ts +0 -80
- package/packages/presentation/src/presentation-contract.test.ts +0 -63
- package/packages/tmux-bridge/src/runner.test.ts +0 -614
- package/packages/tmux-bridge/src/targeting.test.ts +0 -107
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmux-ide",
|
|
3
|
-
"version": "2.9.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.9.4",
|
|
4
|
+
"description": "The open-source workspace for coding agents: run Claude Code, Codex and other agents in tmux with live status",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tmux-ide": "bin/cli.js"
|
|
@@ -25,7 +25,15 @@
|
|
|
25
25
|
"packages/presentation/package.json",
|
|
26
26
|
"packages/tmux-bridge/src",
|
|
27
27
|
"packages/tmux-bridge/package.json",
|
|
28
|
-
"packages/contracts/package.json"
|
|
28
|
+
"packages/contracts/package.json",
|
|
29
|
+
"!packages/*/src/**/*.test.*",
|
|
30
|
+
"!packages/*/src/**/*.spec.*",
|
|
31
|
+
"!packages/*/src/**/__tests__/**",
|
|
32
|
+
"!packages/*/src/**/__snapshots__/**",
|
|
33
|
+
"!packages/daemon/dist/**/*.test.*",
|
|
34
|
+
"!packages/daemon/dist/**/*.spec.*",
|
|
35
|
+
"!packages/daemon/dist/**/__tests__/**",
|
|
36
|
+
"!packages/daemon/dist/**/__snapshots__/**"
|
|
29
37
|
],
|
|
30
38
|
"scripts": {
|
|
31
39
|
"build": "pnpm build:cli",
|
|
@@ -41,8 +49,8 @@
|
|
|
41
49
|
"dev:instance": "node scripts/development-instance.mjs",
|
|
42
50
|
"dev:pilotty": "node scripts/pilotty-dev.mjs",
|
|
43
51
|
"dev:web": "node scripts/dev-web.mjs",
|
|
44
|
-
"test": "pnpm
|
|
45
|
-
"test:unit": "pnpm -r --workspace-concurrency=1 --filter @tmux-ide/daemon --filter @tmux-ide/contracts --filter @tmux-ide/core --filter @tmux-ide/daemon-client --filter @tmux-ide/sdk --filter @tmux-ide/desktop-renderer --filter @tmux-ide/
|
|
52
|
+
"test": "pnpm run test:unit",
|
|
53
|
+
"test:unit": "pnpm run build:cli && pnpm -r --workspace-concurrency=1 --filter @tmux-ide/daemon --filter @tmux-ide/contracts --filter @tmux-ide/core --filter @tmux-ide/daemon-client --filter @tmux-ide/sdk --filter @tmux-ide/desktop-renderer --filter @tmux-ide/electron-shell run test",
|
|
46
54
|
"test:daemon-bun": "bun test ./packages/daemon/src/lib/canonical-daemon-supervision.test.ts ./packages/daemon/src/lib/canonical-daemon.test.ts ./packages/daemon/src/lib/auth/middleware.test.ts ./packages/daemon/src/command-center/actions/handlers/daemon-shutdown.test.ts ./packages/daemon/src/command-center/resources/application-shell.test.ts ./packages/daemon/src/command-center/resources/agent-graph-overlay.test.ts ./packages/daemon/src/tui/mirror/runtime/runtime-layout-presentation.test.ts ./packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.test.ts ./packages/daemon/src/tui/mirror/runtime/terminal-pane-input-router.test.ts",
|
|
47
55
|
"lint": "eslint bin scripts packages/contracts/src packages/core/src packages/daemon-client/src packages/sdk/src packages/tmux-bridge/src packages/daemon/src",
|
|
48
56
|
"lint:workspace": "turbo run lint",
|
|
@@ -64,7 +72,9 @@
|
|
|
64
72
|
"postinstall": "node scripts/postinstall.js",
|
|
65
73
|
"docs": "turbo run dev --filter=@tmux-ide/docs",
|
|
66
74
|
"demo:tui": "bun --preload @opentui/solid/preload docs/scripts/render-tui-demo.tsx",
|
|
67
|
-
"
|
|
75
|
+
"demo:font": "node docs/scripts/tui-demo-font/build.mjs",
|
|
76
|
+
"readme:media": "node docs/scripts/readme-media.mjs",
|
|
77
|
+
"test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./packages/daemon/src/tui/mirror/automatic-contrast-renderer.test.tsx ./packages/daemon/src/tui/mirror/pane-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/native-grid-projection-renderer.test.tsx ./packages/daemon/src/tui/mirror/widget-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/missions-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/recipes-gallery-renderer.test.tsx ./packages/daemon/src/tui/mirror/shell-chrome-renderer.test.tsx ./packages/daemon/src/tui/mirror/sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/home-files-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/changes-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/activity-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/files/session-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-terminal-workspace-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-view-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-root-error-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-backpressure-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-demand-cadence-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/files-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/changes-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/missions-activity-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/dialogs-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/optional-feature-registry-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/palette-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/pane-scoped-terminal-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/features/rich-preview/feature.test.ts ./packages/daemon/src/tui/mirror/runtime/rich-preview-optional-feature-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/application-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/pane-frame-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-chrome-view.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-window-strip-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-shell-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/workbench-dock-dual-host-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/agent-terminal-canvas-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/command-palette-surface-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/opentui-insertion-stability-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-home-renderer.test.tsx ./packages/daemon/src/tui/mirror/workspace/terminal-pane-header-polish-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-roster-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-home-agent-flow-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/guided-tour-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/pane-interaction-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-machine-sidebar-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-add-machine-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-new-agent-dialog-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-switcher-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-palette-preview-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-palette-renderer.test.tsx ./packages/daemon/src/tui/mirror/runtime/application-fleet-session-actions-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/agent-team-renderer.test.tsx ./scripts/solid-conditional-props.test.tsx",
|
|
68
78
|
"test:tui-smoke": "bun scripts/smoke-tui-missions.mjs",
|
|
69
79
|
"test:tui-live": "node scripts/tui-testdrive.mjs smoke",
|
|
70
80
|
"test:tui-testdrive": "node --test scripts/lib/tui-testdrive-clipboard-hook.test.mjs scripts/lib/tui-testdrive-input.test.mjs",
|
|
@@ -104,7 +114,11 @@
|
|
|
104
114
|
},
|
|
105
115
|
"keywords": [
|
|
106
116
|
"tmux",
|
|
107
|
-
"
|
|
117
|
+
"coding-agents",
|
|
118
|
+
"claude-code",
|
|
119
|
+
"codex",
|
|
120
|
+
"ai-agents",
|
|
121
|
+
"agent-teams",
|
|
108
122
|
"terminal",
|
|
109
123
|
"workspace",
|
|
110
124
|
"developer-tools"
|
|
@@ -116,7 +130,7 @@
|
|
|
116
130
|
"type": "git",
|
|
117
131
|
"url": "git+https://github.com/wavyrai/tmux-ide.git"
|
|
118
132
|
},
|
|
119
|
-
"homepage": "https://
|
|
133
|
+
"homepage": "https://tmux-ide.com",
|
|
120
134
|
"bugs": {
|
|
121
135
|
"url": "https://github.com/wavyrai/tmux-ide/issues"
|
|
122
136
|
},
|
|
@@ -139,6 +153,7 @@
|
|
|
139
153
|
"@eslint/js": "^10.0.1",
|
|
140
154
|
"@opentui/core": "^0.5.1",
|
|
141
155
|
"@opentui/solid": "^0.5.1",
|
|
156
|
+
"@tmux-ide/contracts": "workspace:*",
|
|
142
157
|
"@tsconfig/bun": "^1.0.10",
|
|
143
158
|
"@types/node": "^25.5.0",
|
|
144
159
|
"@types/ws": "^8.18.1",
|
|
@@ -53,3 +53,22 @@ export const SavedMachineRegistrySchema = z
|
|
|
53
53
|
});
|
|
54
54
|
});
|
|
55
55
|
export type SavedMachineRegistry = z.infer<typeof SavedMachineRegistrySchema>;
|
|
56
|
+
|
|
57
|
+
/** Local owner actions; never a request to stop a remote daemon or tmux session. */
|
|
58
|
+
export const SavedMachineMutationSchema = z.discriminatedUnion("operation", [
|
|
59
|
+
z.strictObject({
|
|
60
|
+
id: SavedMachineIdSchema,
|
|
61
|
+
operation: z.enum(["enable", "disable", "remove"]),
|
|
62
|
+
}),
|
|
63
|
+
z.strictObject({
|
|
64
|
+
id: SavedMachineIdSchema,
|
|
65
|
+
operation: z.literal("edit"),
|
|
66
|
+
patch: SavedMachineSchema.pick({ label: true, sshTarget: true })
|
|
67
|
+
.partial()
|
|
68
|
+
.refine(
|
|
69
|
+
(patch) => patch.label !== undefined || patch.sshTarget !== undefined,
|
|
70
|
+
"Provide a label or SSH target",
|
|
71
|
+
),
|
|
72
|
+
}),
|
|
73
|
+
]);
|
|
74
|
+
export type SavedMachineMutation = z.infer<typeof SavedMachineMutationSchema>;
|
|
@@ -13,7 +13,7 @@ export type TerminalReplicaCellAttributes = z.infer<typeof TerminalReplicaCellAt
|
|
|
13
13
|
export const TerminalReplicaCellSchemaZ = z
|
|
14
14
|
.object({
|
|
15
15
|
grapheme: z.string(),
|
|
16
|
-
width: z.
|
|
16
|
+
width: z.literal([0, 1, 2]),
|
|
17
17
|
foreground: TerminalReplicaColorSchemaZ,
|
|
18
18
|
background: TerminalReplicaColorSchemaZ,
|
|
19
19
|
attributes: TerminalReplicaCellAttributesSchemaZ,
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
"scripts": {
|
|
25
25
|
"lint": "eslint src",
|
|
26
26
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"test": "vitest run"
|
|
27
|
+
"test": "vitest run --exclude src/terminal-compact-cold.test.ts && vitest run src/terminal-compact-cold.test.ts --maxWorkers=1"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@tmux-ide/contracts": "workspace:*"
|
|
30
|
+
"@tmux-ide/contracts": "workspace:*",
|
|
31
|
+
"zod": "^4.3.6"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"typescript": "^5.9.3",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { isOwnedTerminalReplicaRow } from "./terminal-replica-owned-row.ts";
|
|
1
3
|
import {
|
|
2
4
|
TERMINAL_DELIVERY_CHUNK_BYTES,
|
|
3
5
|
TERMINAL_DELIVERY_MAX_REPRESENTATION_BYTES,
|
|
@@ -209,6 +211,43 @@ type CompactColor = 0 | readonly [1 | 2, number];
|
|
|
209
211
|
type CompactCellRun = [number, string, 0 | 1 | 2, CompactColor, CompactColor, number];
|
|
210
212
|
type CompactRow = readonly [0 | 1, readonly CompactCellRun[]];
|
|
211
213
|
|
|
214
|
+
// Repeated seeds share immutable core-owned rows. Validate each such row
|
|
215
|
+
// with the strict contract before remembering it; ownership alone is not enough.
|
|
216
|
+
// External rows still use the ordinary schema and detached parse result.
|
|
217
|
+
const validatedOwnedEncodingRows = new WeakSet<TerminalReplicaRow>();
|
|
218
|
+
const CompactEncodingRowSchema = z.union([
|
|
219
|
+
z.custom<TerminalReplicaRow>((input) => {
|
|
220
|
+
if (!isOwnedTerminalReplicaRow(input)) return false;
|
|
221
|
+
if (validatedOwnedEncodingRows.has(input)) return true;
|
|
222
|
+
if (!TerminalReplicaRowSchemaZ.safeParse(input).success) return false;
|
|
223
|
+
validatedOwnedEncodingRows.add(input);
|
|
224
|
+
return true;
|
|
225
|
+
}),
|
|
226
|
+
TerminalReplicaRowSchemaZ,
|
|
227
|
+
]);
|
|
228
|
+
const CompactEncodingPayloadSchema = z.discriminatedUnion("frame", [
|
|
229
|
+
TerminalSemanticDeliveryPayloadSchemaZ.options[0].extend({
|
|
230
|
+
snapshot: TerminalReplicaSnapshotSchemaZ.extend({
|
|
231
|
+
grid: z.array(CompactEncodingRowSchema),
|
|
232
|
+
history: z.array(CompactEncodingRowSchema),
|
|
233
|
+
}),
|
|
234
|
+
}),
|
|
235
|
+
TerminalSemanticDeliveryPayloadSchemaZ.options[1],
|
|
236
|
+
TerminalSemanticDeliveryPayloadSchemaZ.options[2],
|
|
237
|
+
]);
|
|
238
|
+
|
|
239
|
+
function hasOwnedSeedRow(input: TerminalSemanticDeliveryPayload): boolean {
|
|
240
|
+
// Select the optimized schema without reading user accessors. Its row union
|
|
241
|
+
// still validates every unowned row if a seed mixes owned and external data.
|
|
242
|
+
const snapshot = Object.getOwnPropertyDescriptor(input, "snapshot")?.value;
|
|
243
|
+
if (typeof snapshot !== "object" || snapshot === null) return false;
|
|
244
|
+
const grid = Object.getOwnPropertyDescriptor(snapshot, "grid")?.value;
|
|
245
|
+
return (
|
|
246
|
+
Array.isArray(grid) &&
|
|
247
|
+
isOwnedTerminalReplicaRow(Object.getOwnPropertyDescriptor(grid, "0")?.value)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
212
251
|
/**
|
|
213
252
|
* A negotiated exact semantic representation. It changes only the wire shape:
|
|
214
253
|
* decode always returns the same strict canonical payload and hash authority.
|
|
@@ -216,7 +255,9 @@ type CompactRow = readonly [0 | 1, readonly CompactCellRun[]];
|
|
|
216
255
|
export function encodeCompactSemanticTerminalUpdate(
|
|
217
256
|
input: TerminalSemanticDeliveryPayload,
|
|
218
257
|
): Uint8Array {
|
|
219
|
-
const update =
|
|
258
|
+
const update = (
|
|
259
|
+
hasOwnedSeedRow(input) ? CompactEncodingPayloadSchema : TerminalSemanticDeliveryPayloadSchemaZ
|
|
260
|
+
).parse(input);
|
|
220
261
|
// Compact payloads contain only arrays and primitives below this object.
|
|
221
262
|
// Insert root keys in canonical order so the native serializer emits exactly
|
|
222
263
|
// the same representation without recursively allocating JSON fragments.
|
|
@@ -344,7 +385,10 @@ export async function encodeCompactSemanticTerminalUpdateCooperatively(
|
|
|
344
385
|
for (const rows of [grid, history]) {
|
|
345
386
|
write("[");
|
|
346
387
|
for (let index = 0; index < rows.length; index++) {
|
|
347
|
-
const
|
|
388
|
+
const row = rows[index]!;
|
|
389
|
+
const owned = isOwnedTerminalReplicaRow(row);
|
|
390
|
+
const validatedRow = owned && validatedOwnedEncodingRows.has(row);
|
|
391
|
+
const { cells, ...rowHeaderInput } = row;
|
|
348
392
|
const rowHeader = CompactRowHeaderSchema.parse(rowHeaderInput);
|
|
349
393
|
if (++rowCount > COMPACT_MAX_ROWS || !Array.isArray(cells) || cells.length !== metadata.cols)
|
|
350
394
|
compactEncodingLimit();
|
|
@@ -361,7 +405,8 @@ export async function encodeCompactSemanticTerminalUpdateCooperatively(
|
|
|
361
405
|
wroteRun = true;
|
|
362
406
|
};
|
|
363
407
|
for (let offset = 0; offset < cells.length; offset += 256) {
|
|
364
|
-
const
|
|
408
|
+
const slice = cells.slice(offset, offset + 256);
|
|
409
|
+
const validated = validatedRow ? slice : CompactCellSliceSchema.parse(slice);
|
|
365
410
|
for (const cell of validated) {
|
|
366
411
|
const encoded = compactCell(cell);
|
|
367
412
|
if (prior && compactRunCellEqual(prior, encoded)) prior[0]++;
|
|
@@ -381,6 +426,9 @@ export async function encodeCompactSemanticTerminalUpdateCooperatively(
|
|
|
381
426
|
}
|
|
382
427
|
emitRun();
|
|
383
428
|
write("]]");
|
|
429
|
+
// Header and every cell have passed the strict row contract, in bounded
|
|
430
|
+
// slices. Only a completed immutable row may be reused by either encoder.
|
|
431
|
+
if (owned) validatedOwnedEncodingRows.add(row);
|
|
384
432
|
}
|
|
385
433
|
write("],");
|
|
386
434
|
}
|
|
@@ -50,9 +50,9 @@ function initialize(): NativeHasher | null {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/** Internal optional accelerator. Canonical serialization remains the caller's responsibility. */
|
|
53
|
-
export function createBufferedFnv64(): BufferedFnv64 | null {
|
|
53
|
+
export function createBufferedFnv64(initialState = OFFSET_BASIS): BufferedFnv64 | null {
|
|
54
54
|
const hasher = initialize();
|
|
55
|
-
return hasher ? new BufferedHasher(hasher) : null;
|
|
55
|
+
return hasher ? new BufferedHasher(hasher, initialState) : null;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
class BufferedHasher implements BufferedFnv64 {
|
|
@@ -61,8 +61,9 @@ class BufferedHasher implements BufferedFnv64 {
|
|
|
61
61
|
#length = 0;
|
|
62
62
|
#state = OFFSET_BASIS;
|
|
63
63
|
|
|
64
|
-
constructor(hasher: NativeHasher) {
|
|
64
|
+
constructor(hasher: NativeHasher, initialState: bigint) {
|
|
65
65
|
this.#native = hasher;
|
|
66
|
+
this.#state = BigInt.asUintN(64, initialState);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
#flush(): void {
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import type { TerminalReplicaColor, TerminalReplicaRow } from "@tmux-ide/contracts";
|
|
2
|
-
import { createBufferedFnv64 } from "./terminal-fnv64-wasm.ts";
|
|
2
|
+
import { createBufferedFnv64, type BufferedFnv64 } from "./terminal-fnv64-wasm.ts";
|
|
3
3
|
|
|
4
4
|
const ROW_HASH_CACHE = new WeakMap<object, string>();
|
|
5
5
|
const DEEPLY_FROZEN_ROWS = new WeakSet<object>();
|
|
6
6
|
const UTF8_ENCODER = new TextEncoder();
|
|
7
7
|
|
|
8
|
+
interface CanonicalKeyOrder {
|
|
9
|
+
readonly original: string[];
|
|
10
|
+
readonly sorted: string[];
|
|
11
|
+
readonly tokens: readonly (string | null)[] | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
15
|
+
* Streaming FNV-1a64 writer for the canonical terminal encoding.
|
|
10
16
|
*
|
|
11
17
|
* Keep the two 32-bit limbs: BigInt per byte made a unique 5k-row compact
|
|
12
18
|
* delivery monopolize the OpenTUI event loop for almost a second.
|
|
@@ -14,6 +20,58 @@ const UTF8_ENCODER = new TextEncoder();
|
|
|
14
20
|
class CanonicalFnv64 {
|
|
15
21
|
#high = 0xcbf29ce4;
|
|
16
22
|
#low = 0x84222325;
|
|
23
|
+
#remainingJsBytes: number;
|
|
24
|
+
#accelerator: BufferedFnv64 | null = null;
|
|
25
|
+
|
|
26
|
+
constructor(accelerateLargeFrame = false) {
|
|
27
|
+
this.#remainingJsBytes = accelerateLargeFrame ? 64 * 1024 : 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#considerAcceleration(bytes: number): void {
|
|
31
|
+
if (this.#remainingJsBytes <= 0) return;
|
|
32
|
+
this.#remainingJsBytes -= bytes;
|
|
33
|
+
if (this.#remainingJsBytes <= 0) {
|
|
34
|
+
// Transfer the exact rolling state once; never restart or reserialize.
|
|
35
|
+
// Each writer owns its buffer across cooperative yields. Blocked WASM
|
|
36
|
+
// keeps the original JS path, without repeated initialization attempts.
|
|
37
|
+
this.#accelerator = createBufferedFnv64((BigInt(this.#high) << 32n) | BigInt(this.#low));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Per-hash only: terminal objects repeat a handful of small field layouts.
|
|
41
|
+
// Reuse sorted keys and short ASCII key tokens without retaining records,
|
|
42
|
+
// values, or future calls. Tokens share the 16-layout/32-key cache bound.
|
|
43
|
+
#keyOrders: CanonicalKeyOrder[] | null = null;
|
|
44
|
+
|
|
45
|
+
keyOrder(record: Record<string, unknown>): CanonicalKeyOrder {
|
|
46
|
+
const keys = Object.keys(record);
|
|
47
|
+
if (keys.length > 32) return { original: keys, sorted: keys.sort(), tokens: null };
|
|
48
|
+
for (const order of this.#keyOrders ?? []) {
|
|
49
|
+
if (order.original.length !== keys.length) continue;
|
|
50
|
+
let matches = true;
|
|
51
|
+
for (let index = 0; index < keys.length; index++) {
|
|
52
|
+
if (order.original[index] !== keys[index]) {
|
|
53
|
+
matches = false;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (matches) return order;
|
|
58
|
+
}
|
|
59
|
+
if ((this.#keyOrders?.length ?? 0) >= 16)
|
|
60
|
+
return { original: keys, sorted: keys.sort(), tokens: null };
|
|
61
|
+
const sorted = keys.length < 2 ? keys : keys.slice().sort();
|
|
62
|
+
const order = {
|
|
63
|
+
original: keys,
|
|
64
|
+
sorted,
|
|
65
|
+
tokens: sorted.map((key) => {
|
|
66
|
+
if (key.length > 32) return null;
|
|
67
|
+
for (let index = 0; index < key.length; index++)
|
|
68
|
+
if (key.charCodeAt(index) > 0x7f) return null;
|
|
69
|
+
return `s${key.length}:${key};`;
|
|
70
|
+
}),
|
|
71
|
+
};
|
|
72
|
+
(this.#keyOrders ??= []).push(order);
|
|
73
|
+
return order;
|
|
74
|
+
}
|
|
17
75
|
|
|
18
76
|
#byte(value: number): void {
|
|
19
77
|
const low = (this.#low ^ value) >>> 0;
|
|
@@ -24,6 +82,8 @@ class CanonicalFnv64 {
|
|
|
24
82
|
}
|
|
25
83
|
|
|
26
84
|
ascii(value: string): number {
|
|
85
|
+
this.#considerAcceleration(value.length);
|
|
86
|
+
if (this.#accelerator) return this.#accelerator.ascii(value);
|
|
27
87
|
// Canonical color/field tags dominate full-row hashing. Keep the limbs
|
|
28
88
|
// local through each fragment instead of reading/writing fields per byte.
|
|
29
89
|
let high = this.#high;
|
|
@@ -40,7 +100,9 @@ class CanonicalFnv64 {
|
|
|
40
100
|
}
|
|
41
101
|
|
|
42
102
|
bytes(value: Uint8Array): void {
|
|
43
|
-
|
|
103
|
+
this.#considerAcceleration(value.byteLength);
|
|
104
|
+
if (this.#accelerator) this.#accelerator.bytes(value);
|
|
105
|
+
else for (const byte of value) this.#byte(byte);
|
|
44
106
|
}
|
|
45
107
|
|
|
46
108
|
string(value: string): number {
|
|
@@ -56,13 +118,13 @@ class CanonicalFnv64 {
|
|
|
56
118
|
if (ascii) {
|
|
57
119
|
this.ascii(`s${value.length}:`);
|
|
58
120
|
this.ascii(value);
|
|
59
|
-
this
|
|
121
|
+
this.ascii(";");
|
|
60
122
|
return value.length;
|
|
61
123
|
}
|
|
62
124
|
const bytes = UTF8_ENCODER.encode(value);
|
|
63
125
|
this.ascii(`s${bytes.byteLength}:`);
|
|
64
|
-
|
|
65
|
-
this
|
|
126
|
+
this.bytes(bytes);
|
|
127
|
+
this.ascii(";");
|
|
66
128
|
return bytes.byteLength;
|
|
67
129
|
}
|
|
68
130
|
|
|
@@ -99,22 +161,26 @@ class CanonicalFnv64 {
|
|
|
99
161
|
return;
|
|
100
162
|
}
|
|
101
163
|
const record = value as Record<string, unknown>;
|
|
102
|
-
const keys =
|
|
164
|
+
const { sorted: keys, tokens } = this.keyOrder(record);
|
|
103
165
|
this.ascii(`o${keys.length}:`);
|
|
104
|
-
for (
|
|
105
|
-
|
|
166
|
+
for (let index = 0; index < keys.length; index++) {
|
|
167
|
+
const key = keys[index]!;
|
|
168
|
+
const token = tokens?.[index];
|
|
169
|
+
if (token !== null && token !== undefined) this.ascii(token);
|
|
170
|
+
else this.string(key);
|
|
106
171
|
this.value(record[key]);
|
|
107
172
|
}
|
|
108
173
|
this.ascii(";");
|
|
109
174
|
}
|
|
110
175
|
|
|
111
176
|
digest(): string {
|
|
177
|
+
if (this.#accelerator) return this.#accelerator.digest();
|
|
112
178
|
return `${this.#high.toString(16).padStart(8, "0")}${this.#low.toString(16).padStart(8, "0")}`;
|
|
113
179
|
}
|
|
114
180
|
}
|
|
115
181
|
|
|
116
182
|
export function hashCanonicalTerminalValue(value: unknown): string {
|
|
117
|
-
const hash = new CanonicalFnv64();
|
|
183
|
+
const hash = new CanonicalFnv64(true);
|
|
118
184
|
hash.value(value);
|
|
119
185
|
return hash.digest();
|
|
120
186
|
}
|
|
@@ -124,7 +190,7 @@ export async function hashCanonicalTerminalValueCooperatively(
|
|
|
124
190
|
yieldControl: () => Promise<void>,
|
|
125
191
|
workPerSlice = 4 * 1_024,
|
|
126
192
|
): Promise<string> {
|
|
127
|
-
const hash = new CanonicalFnv64();
|
|
193
|
+
const hash = new CanonicalFnv64(true);
|
|
128
194
|
let work = 0;
|
|
129
195
|
const checkpoint = (amount: number): boolean => {
|
|
130
196
|
work += amount;
|
|
@@ -175,7 +241,7 @@ export async function hashCanonicalTerminalValueCooperatively(
|
|
|
175
241
|
continue;
|
|
176
242
|
}
|
|
177
243
|
const record = entry as Record<string, unknown>;
|
|
178
|
-
const keys =
|
|
244
|
+
const keys = hash.keyOrder(record).sorted;
|
|
179
245
|
if (checkpoint(hash.ascii(`o${keys.length}:`) + keys.length)) await yieldControl();
|
|
180
246
|
stack.push({ kind: "ascii", value: ";" });
|
|
181
247
|
for (let index = keys.length - 1; index >= 0; index -= 1) {
|
|
@@ -215,6 +281,8 @@ const compactColorKey = (color: TerminalReplicaColor): number =>
|
|
|
215
281
|
/** Package-private per-decode cache for exact canonical cell byte segments. */
|
|
216
282
|
export class TerminalReplicaRunEncodingCache {
|
|
217
283
|
#canonicalBytes = 0;
|
|
284
|
+
#cachedCells = 0;
|
|
285
|
+
#retainedEncodingBytes = 0;
|
|
218
286
|
|
|
219
287
|
createHash(): CanonicalFnv64 | NonNullable<ReturnType<typeof createBufferedFnv64>> {
|
|
220
288
|
// Small deliveries benefit more from row reuse than from crossing into
|
|
@@ -238,17 +306,14 @@ export class TerminalReplicaRunEncodingCache {
|
|
|
238
306
|
readonly allocatedBytes: number;
|
|
239
307
|
readonly cacheMiss: boolean;
|
|
240
308
|
} {
|
|
241
|
-
const widths = this.#entries.get(cell.grapheme) ?? new Map();
|
|
242
|
-
this.#entries.set(cell.grapheme, widths);
|
|
243
309
|
const foregroundKey = compactColorKey(cell.foreground);
|
|
244
|
-
const foregrounds = widths.get(cell.width) ?? new Map();
|
|
245
|
-
widths.set(cell.width, foregrounds);
|
|
246
310
|
const backgroundKey = compactColorKey(cell.background);
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
311
|
+
const cached = this.#entries
|
|
312
|
+
.get(cell.grapheme)
|
|
313
|
+
?.get(cell.width)
|
|
314
|
+
?.get(foregroundKey)
|
|
315
|
+
?.get(backgroundKey)
|
|
316
|
+
?.get(cell.attributes);
|
|
252
317
|
if (cached) return { prepared: cached, allocatedBytes: 0, cacheMiss: false };
|
|
253
318
|
const graphemeBytes = UTF8_ENCODER.encode(cell.grapheme);
|
|
254
319
|
const numberText = (value: number): string => {
|
|
@@ -268,30 +333,63 @@ export class TerminalReplicaRunEncodingCache {
|
|
|
268
333
|
cell.background,
|
|
269
334
|
)}${numberText(cell.attributes)};`,
|
|
270
335
|
});
|
|
271
|
-
|
|
336
|
+
// A transaction may contain arbitrary text and RGB values. Bound both
|
|
337
|
+
// entry count and retained string/byte payload; uncached cells hash identically.
|
|
338
|
+
const retainedBytes =
|
|
339
|
+
graphemeBytes.byteLength +
|
|
340
|
+
2 * (cell.grapheme.length + prepared.prefix.length + prepared.suffix.length);
|
|
341
|
+
if (this.#cachedCells < 1_024 && this.#retainedEncodingBytes + retainedBytes <= 64 * 1_024) {
|
|
342
|
+
const widths = this.#entries.get(cell.grapheme) ?? new Map();
|
|
343
|
+
this.#entries.set(cell.grapheme, widths);
|
|
344
|
+
const foregrounds = widths.get(cell.width) ?? new Map();
|
|
345
|
+
widths.set(cell.width, foregrounds);
|
|
346
|
+
const backgrounds = foregrounds.get(foregroundKey) ?? new Map();
|
|
347
|
+
foregrounds.set(foregroundKey, backgrounds);
|
|
348
|
+
const attributes = backgrounds.get(backgroundKey) ?? new Map();
|
|
349
|
+
backgrounds.set(backgroundKey, attributes);
|
|
350
|
+
attributes.set(cell.attributes, prepared);
|
|
351
|
+
this.#cachedCells++;
|
|
352
|
+
this.#retainedEncodingBytes += retainedBytes;
|
|
353
|
+
}
|
|
272
354
|
return { prepared, allocatedBytes: graphemeBytes.byteLength, cacheMiss: true };
|
|
273
355
|
}
|
|
274
356
|
}
|
|
275
357
|
|
|
276
|
-
export function hashTerminalReplicaRowCached(
|
|
358
|
+
export function hashTerminalReplicaRowCached(
|
|
359
|
+
row: TerminalReplicaRow,
|
|
360
|
+
onMiss?: () => void,
|
|
361
|
+
encodingCache?: TerminalReplicaRunEncodingCache,
|
|
362
|
+
): string {
|
|
277
363
|
const cached = ROW_HASH_CACHE.get(row);
|
|
278
364
|
if (cached) return cached;
|
|
279
365
|
onMiss?.();
|
|
280
|
-
const hash = new CanonicalFnv64();
|
|
366
|
+
const hash = encodingCache?.createHash() ?? new CanonicalFnv64();
|
|
367
|
+
let canonicalBytes = 10 + String(row.cells.length).length;
|
|
281
368
|
hash.ascii("a2:");
|
|
282
369
|
hash.boolean(row.wrapped);
|
|
283
370
|
hash.ascii(`a${row.cells.length}:`);
|
|
284
371
|
for (const cell of row.cells) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
372
|
+
if (encodingCache) {
|
|
373
|
+
const { prepared } = encodingCache.prepare(cell);
|
|
374
|
+
hash.ascii(prepared.prefix);
|
|
375
|
+
hash.bytes(prepared.graphemeBytes);
|
|
376
|
+
hash.ascii(prepared.suffix);
|
|
377
|
+
canonicalBytes +=
|
|
378
|
+
prepared.prefix.length + prepared.graphemeBytes.length + prepared.suffix.length;
|
|
379
|
+
} else {
|
|
380
|
+
const direct = hash as CanonicalFnv64;
|
|
381
|
+
direct.ascii("a5:");
|
|
382
|
+
direct.string(cell.grapheme);
|
|
383
|
+
direct.number(cell.width);
|
|
384
|
+
writeColor(direct, cell.foreground);
|
|
385
|
+
writeColor(direct, cell.background);
|
|
386
|
+
direct.number(cell.attributes);
|
|
387
|
+
direct.ascii(";");
|
|
388
|
+
}
|
|
292
389
|
}
|
|
293
390
|
hash.ascii(";;");
|
|
294
391
|
const digest = hash.digest();
|
|
392
|
+
encodingCache?.recordCanonicalBytes(canonicalBytes);
|
|
295
393
|
if (isTerminalReplicaRowDeeplyFrozen(row)) {
|
|
296
394
|
DEEPLY_FROZEN_ROWS.add(row);
|
|
297
395
|
ROW_HASH_CACHE.set(row, digest);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TerminalReplicaRow } from "@tmux-ide/contracts";
|
|
2
|
+
|
|
3
|
+
// Ownership proves immutable, ordinary data properties, not schema validity.
|
|
4
|
+
// There is no registration API: callers can only obtain ownership by copying.
|
|
5
|
+
const OWNED_ROWS = new WeakSet<TerminalReplicaRow>();
|
|
6
|
+
|
|
7
|
+
export function isOwnedTerminalReplicaRow(row: unknown): row is TerminalReplicaRow {
|
|
8
|
+
return typeof row === "object" && row !== null && OWNED_ROWS.has(row as TerminalReplicaRow);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function freezeOwnedTerminalReplicaRow(row: TerminalReplicaRow): TerminalReplicaRow {
|
|
12
|
+
if (OWNED_ROWS.has(row)) return row;
|
|
13
|
+
const frozen = Object.freeze({
|
|
14
|
+
wrapped: row.wrapped,
|
|
15
|
+
cells: Object.freeze(
|
|
16
|
+
Array.from(row.cells, (cell) =>
|
|
17
|
+
Object.freeze({
|
|
18
|
+
...cell,
|
|
19
|
+
foreground: Object.freeze({ ...cell.foreground }),
|
|
20
|
+
background: Object.freeze({ ...cell.background }),
|
|
21
|
+
}),
|
|
22
|
+
),
|
|
23
|
+
),
|
|
24
|
+
}) as unknown as TerminalReplicaRow;
|
|
25
|
+
OWNED_ROWS.add(frozen);
|
|
26
|
+
return frozen;
|
|
27
|
+
}
|