tmux-ide 2.9.0-beta.20 → 2.9.0-beta.21

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.
Files changed (49) hide show
  1. package/bin/cli.js +100 -8
  2. package/package.json +3 -2
  3. package/packages/daemon/dist/command-center/diagnostics.js +65 -0
  4. package/packages/daemon/dist/command-center/log-stream.js +9 -0
  5. package/packages/daemon/dist/command-center/server.js +7 -0
  6. package/packages/daemon/dist/lib/app-config.js +4 -2
  7. package/packages/daemon/dist/lib/soak-diagnostics.js +124 -0
  8. package/packages/daemon/dist/lib/soak-verdict.js +185 -35
  9. package/packages/daemon/dist/lib/terminal-host-color.js +33 -0
  10. package/packages/daemon/dist/tui/mirror/automatic-contrast.js +161 -0
  11. package/packages/daemon/dist/tui/mirror/open-tui-workspace-runtime-port.js +9 -3
  12. package/packages/daemon/dist/tui/mirror/pane-surface.jsx +7 -5
  13. package/packages/daemon/dist/tui/mirror/resize-transaction.js +48 -20
  14. package/packages/daemon/dist/tui/mirror/runtime/application-appearance-owner.js +40 -6
  15. package/packages/daemon/dist/tui/mirror/runtime/application-machine-sidebar.jsx +63 -42
  16. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +138 -67
  17. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-palette-owner.js +44 -28
  18. package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace.jsx +49 -11
  19. package/packages/daemon/dist/tui/mirror/runtime/semantic-shell-viewport-resize.js +140 -2
  20. package/packages/daemon/dist/tui/mirror/runtime/workspace-terminal-fast-lane.js +3 -1
  21. package/packages/daemon/dist/tui/mirror/semantic-pane-render-source.js +2 -1
  22. package/packages/daemon/dist/tui/mirror/theme.js +4 -31
  23. package/packages/daemon/dist/tui/mirror/workspace/terminal-pane-header.jsx +15 -8
  24. package/packages/daemon/src/command-center/diagnostics.ts +75 -0
  25. package/packages/daemon/src/command-center/log-stream.ts +8 -0
  26. package/packages/daemon/src/command-center/server.ts +8 -0
  27. package/packages/daemon/src/lib/app-config.ts +11 -3
  28. package/packages/daemon/src/lib/soak-diagnostics.ts +183 -0
  29. package/packages/daemon/src/lib/soak-verdict.ts +314 -23
  30. package/packages/daemon/src/lib/terminal-host-color.ts +43 -0
  31. package/packages/daemon/src/tui/mirror/automatic-contrast.ts +180 -0
  32. package/packages/daemon/src/tui/mirror/open-tui-workspace-runtime-port.ts +26 -5
  33. package/packages/daemon/src/tui/mirror/pane-surface.tsx +9 -8
  34. package/packages/daemon/src/tui/mirror/resize-transaction.ts +46 -22
  35. package/packages/daemon/src/tui/mirror/runtime/application-appearance-owner.ts +45 -6
  36. package/packages/daemon/src/tui/mirror/runtime/application-machine-sidebar.tsx +99 -75
  37. package/packages/daemon/src/tui/mirror/runtime/application-root-v2.tsx +1 -0
  38. package/packages/daemon/src/tui/mirror/runtime/application-shell-overlays.tsx +9 -2
  39. package/packages/daemon/src/tui/mirror/runtime/application-shell-view.tsx +2 -0
  40. package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +148 -69
  41. package/packages/daemon/src/tui/mirror/runtime/application-terminal-palette-owner.ts +55 -28
  42. package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace.tsx +59 -17
  43. package/packages/daemon/src/tui/mirror/runtime/semantic-shell-viewport-resize.ts +151 -3
  44. package/packages/daemon/src/tui/mirror/runtime/workspace-terminal-fast-lane.ts +3 -1
  45. package/packages/daemon/src/tui/mirror/semantic-pane-render-source.ts +2 -1
  46. package/packages/daemon/src/tui/mirror/theme.ts +4 -39
  47. package/packages/daemon/src/tui/mirror/workspace/terminal-pane-header.tsx +21 -8
  48. package/packages/daemon-client/src/terminal-fast-lane.test.ts +18 -0
  49. package/packages/daemon-client/src/terminal-fast-lane.ts +7 -2
package/bin/cli.js CHANGED
@@ -9273,7 +9273,12 @@ function parseAppConfig(input) {
9273
9273
  }
9274
9274
  },
9275
9275
  theme: {
9276
- mode: pickChoice(theme.mode, ["dark", "light", "system"], D.theme.mode),
9276
+ automaticContrast: pickBool(theme.automaticContrast, D.theme.automaticContrast),
9277
+ mode: pickChoice(
9278
+ theme.mode,
9279
+ ["dark", "light", "system"],
9280
+ findVisualThemePreset(theme.preset)?.appearance ?? D.theme.mode
9281
+ ),
9277
9282
  ...findVisualThemePreset(theme.preset) ? { preset: String(theme.preset) } : {},
9278
9283
  accent: pickString(theme.accent, D.theme.accent),
9279
9284
  muted: pickString(theme.muted, D.theme.muted),
@@ -9399,7 +9404,8 @@ var init_app_config = __esm({
9399
9404
  panels: { explorer: "M-e", changes: "M-g", config: "M-," }
9400
9405
  },
9401
9406
  theme: {
9402
- mode: "dark",
9407
+ automaticContrast: true,
9408
+ mode: "system",
9403
9409
  accent: "colour75",
9404
9410
  muted: "colour240",
9405
9411
  fg: "colour250",
@@ -10969,7 +10975,7 @@ var require_package = __commonJS({
10969
10975
  "package.json"(exports, module) {
10970
10976
  module.exports = {
10971
10977
  name: "tmux-ide",
10972
- version: "2.9.0-beta.20",
10978
+ version: "2.9.0-beta.21",
10973
10979
  description: "A visual, agent-aware IDE for any tmux session, with optional workspace presets",
10974
10980
  type: "module",
10975
10981
  bin: {
@@ -11008,6 +11014,7 @@ var require_package = __commonJS({
11008
11014
  typecheck: 'echo "root typecheck deferred to per-package turbo run"',
11009
11015
  dev: "node bin/cli.js",
11010
11016
  "dev:instance": "node scripts/development-instance.mjs",
11017
+ "dev:pilotty": "node scripts/pilotty-dev.mjs",
11011
11018
  "dev:web": "node scripts/dev-web.mjs",
11012
11019
  test: "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/web-workspace --filter @tmux-ide/electron-shell run test",
11013
11020
  "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/web-workspace --filter @tmux-ide/electron-shell run test",
@@ -11032,7 +11039,7 @@ var require_package = __commonJS({
11032
11039
  postinstall: "node scripts/postinstall.js",
11033
11040
  docs: "turbo run dev --filter=@tmux-ide/docs",
11034
11041
  "demo:tui": "bun --preload @opentui/solid/preload docs/scripts/render-tui-demo.tsx",
11035
- "test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./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/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-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-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",
11042
+ "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/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-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-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",
11036
11043
  "test:tui-smoke": "bun scripts/smoke-tui-missions.mjs",
11037
11044
  "test:tui-live": "node scripts/tui-testdrive.mjs smoke",
11038
11045
  "test:tui-testdrive": "node --test scripts/lib/tui-testdrive-clipboard-hook.test.mjs scripts/lib/tui-testdrive-input.test.mjs",
@@ -68929,6 +68936,7 @@ async function streamBoundedLogs(stream, options) {
68929
68936
  let unsubscribe = () => {
68930
68937
  };
68931
68938
  let timer = null;
68939
+ let heartbeatTimer = null;
68932
68940
  let cancelWrite = null;
68933
68941
  const cleanup = () => {
68934
68942
  if (closed) return;
@@ -68938,6 +68946,8 @@ async function streamBoundedLogs(stream, options) {
68938
68946
  bytes = 0;
68939
68947
  if (timer) clearTimeout(timer);
68940
68948
  timer = null;
68949
+ if (heartbeatTimer) clearInterval(heartbeatTimer);
68950
+ heartbeatTimer = null;
68941
68951
  wake?.();
68942
68952
  wake = null;
68943
68953
  cancelWrite?.();
@@ -68967,6 +68977,10 @@ async function streamBoundedLogs(stream, options) {
68967
68977
  unsubscribe();
68968
68978
  return;
68969
68979
  }
68980
+ heartbeatTimer = setInterval(() => {
68981
+ if (!closed && queue.length === 0 && !push({ event: "heartbeat", data: "keep-alive" })) abort();
68982
+ }, options.heartbeatIntervalMs ?? 15e3);
68983
+ heartbeatTimer.unref?.();
68970
68984
  try {
68971
68985
  const retained = [];
68972
68986
  let retainedBytes = 0;
@@ -75929,6 +75943,79 @@ var init_workspace_missions_route = __esm({
75929
75943
  }
75930
75944
  });
75931
75945
 
75946
+ // packages/daemon/src/command-center/diagnostics.ts
75947
+ import { performance as performance2 } from "node:perf_hooks";
75948
+ function sampleDaemonDiagnostics(daemon) {
75949
+ const sampledAtMs = Date.now();
75950
+ const memory = process.memoryUsage();
75951
+ const cpu = process.cpuUsage();
75952
+ const eventLoop = performance2.eventLoopUtilization();
75953
+ const activeResources = {
75954
+ Timeout: 0,
75955
+ Immediate: 0,
75956
+ TCPServerWrap: 0,
75957
+ TCPSocketWrap: 0,
75958
+ PipeWrap: 0,
75959
+ ProcessWrap: 0,
75960
+ FSEventWrap: 0,
75961
+ other: 0
75962
+ };
75963
+ const resourceNames = process.getActiveResourcesInfo?.();
75964
+ for (const resource3 of resourceNames ?? []) {
75965
+ if (Object.hasOwn(activeResources, resource3) && resource3 !== "other") {
75966
+ activeResources[resource3]++;
75967
+ } else {
75968
+ activeResources.other++;
75969
+ }
75970
+ }
75971
+ return {
75972
+ daemon: {
75973
+ protocolVersion: daemon.protocolVersion,
75974
+ productVersion: daemon.productVersion,
75975
+ instanceId: daemon.instanceId,
75976
+ startedAt: daemon.startedAt,
75977
+ ...daemon.environmentId !== void 0 ? { environmentId: daemon.environmentId } : {}
75978
+ },
75979
+ pid: process.pid,
75980
+ uptimeMs: process.uptime() * 1e3,
75981
+ sampledAtMs,
75982
+ memory: {
75983
+ rss: memory.rss,
75984
+ heapTotal: memory.heapTotal,
75985
+ heapUsed: memory.heapUsed,
75986
+ external: memory.external,
75987
+ arrayBuffers: memory.arrayBuffers
75988
+ },
75989
+ cpu: { user: cpu.user, system: cpu.system },
75990
+ eventLoop: {
75991
+ idle: eventLoop.idle,
75992
+ active: eventLoop.active,
75993
+ utilization: eventLoop.utilization
75994
+ },
75995
+ activeResources: resourceNames === void 0 ? null : activeResources
75996
+ };
75997
+ }
75998
+ function mountDiagnosticsRoute(app, options) {
75999
+ app.get(
76000
+ "/api/diagnostics",
76001
+ requireOwnerAuthority(options.ownerToken, {
76002
+ whenOwnerless: "unavailable",
76003
+ unavailableMessage: "Diagnostics unavailable",
76004
+ mismatchMessage: "Diagnostics require owner authority"
76005
+ }),
76006
+ (c) => {
76007
+ c.header("Cache-Control", "no-store");
76008
+ return c.json(sampleDaemonDiagnostics(options.daemon));
76009
+ }
76010
+ );
76011
+ }
76012
+ var init_diagnostics = __esm({
76013
+ "packages/daemon/src/command-center/diagnostics.ts"() {
76014
+ "use strict";
76015
+ init_owner_authority();
76016
+ }
76017
+ });
76018
+
75932
76019
  // packages/daemon/src/canonical.ts
75933
76020
  var init_canonical = __esm({
75934
76021
  "packages/daemon/src/canonical.ts"() {
@@ -76535,6 +76622,10 @@ function createApp(options = {}) {
76535
76622
  const healthBootedAt = Date.now();
76536
76623
  const app = new Hono();
76537
76624
  app.use("/*", cors());
76625
+ mountDiagnosticsRoute(app, {
76626
+ daemon: daemonInstanceIdentity,
76627
+ ownerToken: options.remoteAccess?.ownerToken ?? null
76628
+ });
76538
76629
  mountTerminalAttachmentIssueRoute(app, {
76539
76630
  daemonInstanceId: daemonIdentity.instanceId,
76540
76631
  ownerToken: options.remoteAccess?.ownerToken ?? null,
@@ -77716,6 +77807,7 @@ var init_server2 = __esm({
77716
77807
  init_fleet_resource_route();
77717
77808
  init_workspace_missions_route();
77718
77809
  init_owner_authority();
77810
+ init_diagnostics();
77719
77811
  init_startup_readiness_route();
77720
77812
  init_widget_asset_store();
77721
77813
  defaultApplicationShellAppWindowBackend = {
@@ -77796,7 +77888,7 @@ import { randomBytes as randomBytes8, randomUUID as randomUUID23 } from "node:cr
77796
77888
  import { createWriteStream } from "node:fs";
77797
77889
  import { createServer as createServer2 } from "node:http";
77798
77890
  import { createRequire as createRequire3 } from "node:module";
77799
- import { performance as performance2 } from "node:perf_hooks";
77891
+ import { performance as performance3 } from "node:perf_hooks";
77800
77892
  import { WebSocket, WebSocketServer as WebSocketServer4 } from "ws";
77801
77893
  function loadBundledPackage() {
77802
77894
  return requireFromHere2("../../package.json");
@@ -78005,13 +78097,13 @@ function createTakeoverDeadline(timeoutMs) {
78005
78097
  throw new TypeError("Takeover deadline must be a positive finite duration.");
78006
78098
  }
78007
78099
  const controller = new AbortController();
78008
- const expiresAt = performance2.now() + timeoutMs;
78100
+ const expiresAt = performance3.now() + timeoutMs;
78009
78101
  const timer = setTimeout(() => controller.abort(), timeoutMs);
78010
78102
  timer.unref?.();
78011
78103
  return {
78012
78104
  signal: controller.signal,
78013
78105
  expiresAt,
78014
- remainingMs: () => Math.max(0, expiresAt - performance2.now()),
78106
+ remainingMs: () => Math.max(0, expiresAt - performance3.now()),
78015
78107
  dispose: () => clearTimeout(timer)
78016
78108
  };
78017
78109
  }
@@ -78637,7 +78729,7 @@ async function startEmbeddedDaemonGeneration(opts) {
78637
78729
  }
78638
78730
  };
78639
78731
  const diagnostics = {
78640
- nowMicros: () => Math.floor(performance2.now() * 1e3),
78732
+ nowMicros: () => Math.floor(performance3.now() * 1e3),
78641
78733
  createTraceId: randomUUID23,
78642
78734
  publish: publishObserverDiagnostic
78643
78735
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmux-ide",
3
- "version": "2.9.0-beta.20",
3
+ "version": "2.9.0-beta.21",
4
4
  "description": "A visual, agent-aware IDE for any tmux session, with optional workspace presets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,6 +39,7 @@
39
39
  "typecheck": "echo \"root typecheck deferred to per-package turbo run\"",
40
40
  "dev": "node bin/cli.js",
41
41
  "dev:instance": "node scripts/development-instance.mjs",
42
+ "dev:pilotty": "node scripts/pilotty-dev.mjs",
42
43
  "dev:web": "node scripts/dev-web.mjs",
43
44
  "test": "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/web-workspace --filter @tmux-ide/electron-shell run test",
44
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/web-workspace --filter @tmux-ide/electron-shell run test",
@@ -63,7 +64,7 @@
63
64
  "postinstall": "node scripts/postinstall.js",
64
65
  "docs": "turbo run dev --filter=@tmux-ide/docs",
65
66
  "demo:tui": "bun --preload @opentui/solid/preload docs/scripts/render-tui-demo.tsx",
66
- "test:tui-renderer": "bun test --preload @opentui/solid/preload --preload ./packages/daemon/test-support/opentui-renderer-preload.ts ./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/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-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-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",
67
+ "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/application-shell-sidebar-catalog-renderer.test.tsx ./packages/daemon/src/tui/mirror/ui/ui-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-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",
67
68
  "test:tui-smoke": "bun scripts/smoke-tui-missions.mjs",
68
69
  "test:tui-live": "node scripts/tui-testdrive.mjs smoke",
69
70
  "test:tui-testdrive": "node --test scripts/lib/tui-testdrive-clipboard-hook.test.mjs scripts/lib/tui-testdrive-input.test.mjs",
@@ -0,0 +1,65 @@
1
+ import { performance } from "node:perf_hooks";
2
+ import { requireOwnerAuthority } from "./owner-authority.js";
3
+ /** One synchronous, demand-only sample. No observers, discovery, or retained baseline. */
4
+ export function sampleDaemonDiagnostics(daemon) {
5
+ const sampledAtMs = Date.now();
6
+ const memory = process.memoryUsage();
7
+ const cpu = process.cpuUsage();
8
+ const eventLoop = performance.eventLoopUtilization();
9
+ // Fixed output cardinality. Unknown runtime resource labels are never echoed.
10
+ const activeResources = {
11
+ Timeout: 0,
12
+ Immediate: 0,
13
+ TCPServerWrap: 0,
14
+ TCPSocketWrap: 0,
15
+ PipeWrap: 0,
16
+ ProcessWrap: 0,
17
+ FSEventWrap: 0,
18
+ other: 0,
19
+ };
20
+ const resourceNames = process.getActiveResourcesInfo?.();
21
+ for (const resource of resourceNames ?? []) {
22
+ if (Object.hasOwn(activeResources, resource) && resource !== "other") {
23
+ activeResources[resource]++;
24
+ }
25
+ else {
26
+ activeResources.other++;
27
+ }
28
+ }
29
+ return {
30
+ daemon: {
31
+ protocolVersion: daemon.protocolVersion,
32
+ productVersion: daemon.productVersion,
33
+ instanceId: daemon.instanceId,
34
+ startedAt: daemon.startedAt,
35
+ ...(daemon.environmentId !== undefined ? { environmentId: daemon.environmentId } : {}),
36
+ },
37
+ pid: process.pid,
38
+ uptimeMs: process.uptime() * 1000,
39
+ sampledAtMs,
40
+ memory: {
41
+ rss: memory.rss,
42
+ heapTotal: memory.heapTotal,
43
+ heapUsed: memory.heapUsed,
44
+ external: memory.external,
45
+ arrayBuffers: memory.arrayBuffers,
46
+ },
47
+ cpu: { user: cpu.user, system: cpu.system },
48
+ eventLoop: {
49
+ idle: eventLoop.idle,
50
+ active: eventLoop.active,
51
+ utilization: eventLoop.utilization,
52
+ },
53
+ activeResources: resourceNames === undefined ? null : activeResources,
54
+ };
55
+ }
56
+ export function mountDiagnosticsRoute(app, options) {
57
+ app.get("/api/diagnostics", requireOwnerAuthority(options.ownerToken, {
58
+ whenOwnerless: "unavailable",
59
+ unavailableMessage: "Diagnostics unavailable",
60
+ mismatchMessage: "Diagnostics require owner authority",
61
+ }), (c) => {
62
+ c.header("Cache-Control", "no-store");
63
+ return c.json(sampleDaemonDiagnostics(options.daemon));
64
+ });
65
+ }
@@ -8,6 +8,7 @@ export async function streamBoundedLogs(stream, options) {
8
8
  let wake = null;
9
9
  let unsubscribe = () => { };
10
10
  let timer = null;
11
+ let heartbeatTimer = null;
11
12
  let cancelWrite = null;
12
13
  const cleanup = () => {
13
14
  if (closed)
@@ -19,6 +20,9 @@ export async function streamBoundedLogs(stream, options) {
19
20
  if (timer)
20
21
  clearTimeout(timer);
21
22
  timer = null;
23
+ if (heartbeatTimer)
24
+ clearInterval(heartbeatTimer);
25
+ heartbeatTimer = null;
22
26
  wake?.();
23
27
  wake = null;
24
28
  cancelWrite?.();
@@ -51,6 +55,11 @@ export async function streamBoundedLogs(stream, options) {
51
55
  unsubscribe();
52
56
  return;
53
57
  }
58
+ heartbeatTimer = setInterval(() => {
59
+ if (!closed && queue.length === 0 && !push({ event: "heartbeat", data: "keep-alive" }))
60
+ abort();
61
+ }, options.heartbeatIntervalMs ?? 15_000);
62
+ heartbeatTimer.unref?.();
54
63
  try {
55
64
  // Keep newest bounded history, reserving room for gap/bookmark metadata.
56
65
  const retained = [];
@@ -63,6 +63,7 @@ import { mountWorkspaceResourceRoutes } from "./resources/workspace-resource-rou
63
63
  import { mountFleetResourceRoute } from "./resources/fleet-resource-route.js";
64
64
  import { mountWorkspaceMissionsRoute } from "./resources/workspace-missions-route.js";
65
65
  import { ownerAuthorityGate, requireOwnerAuthority } from "./owner-authority.js";
66
+ import { mountDiagnosticsRoute } from "./diagnostics.js";
66
67
  import { mountStartupReadinessRoute, } from "./resources/startup-readiness-route.js";
67
68
  import { readWidgetAsset } from "../lib/widget-asset-store.js";
68
69
  const defaultApplicationShellAppWindowBackend = {
@@ -316,6 +317,12 @@ export function createApp(options = {}) {
316
317
  const app = new Hono();
317
318
  // Allow cross-origin (Next.js dashboard, Tailscale, etc.)
318
319
  app.use("/*", cors());
320
+ // Owner-only reads use the same early routing boundary as issuance below:
321
+ // remote/project credentials neither authorize nor block the owner bearer.
322
+ mountDiagnosticsRoute(app, {
323
+ daemon: daemonInstanceIdentity,
324
+ ownerToken: options.remoteAccess?.ownerToken ?? null,
325
+ });
319
326
  // This exact route carries its own owner-only bearer and correlation gate.
320
327
  // Mount it before remote and project auth so a valid host capability is
321
328
  // neither rejected nor confused with any remotely shared user credential.
@@ -39,7 +39,8 @@ export const DEFAULT_APP_CONFIG = {
39
39
  panels: { explorer: "M-e", changes: "M-g", config: "M-," },
40
40
  },
41
41
  theme: {
42
- mode: "dark",
42
+ automaticContrast: true,
43
+ mode: "system",
43
44
  accent: "colour75",
44
45
  muted: "colour240",
45
46
  fg: "colour250",
@@ -160,7 +161,8 @@ export function parseAppConfig(input) {
160
161
  },
161
162
  },
162
163
  theme: {
163
- mode: pickChoice(theme.mode, ["dark", "light", "system"], D.theme.mode),
164
+ automaticContrast: pickBool(theme.automaticContrast, D.theme.automaticContrast),
165
+ mode: pickChoice(theme.mode, ["dark", "light", "system"], findVisualThemePreset(theme.preset)?.appearance ?? D.theme.mode),
164
166
  ...(findVisualThemePreset(theme.preset) ? { preset: String(theme.preset) } : {}),
165
167
  accent: pickString(theme.accent, D.theme.accent),
166
168
  muted: pickString(theme.muted, D.theme.muted),
@@ -0,0 +1,124 @@
1
+ /** Pure, allowlisted evidence projection. Never retain response bodies or errors. */
2
+ export const DIAGNOSTICS_MAX_BYTES = 16_384;
3
+ export const RESOURCE_KEYS = [
4
+ "Timeout",
5
+ "Immediate",
6
+ "TCPServerWrap",
7
+ "TCPSocketWrap",
8
+ "PipeWrap",
9
+ "ProcessWrap",
10
+ "FSEventWrap",
11
+ "other",
12
+ ];
13
+ export const MEMORY_KEYS = ["rss", "heapTotal", "heapUsed", "external", "arrayBuffers"];
14
+ const object = (v) => !!v && typeof v === "object" && !Array.isArray(v);
15
+ const number = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
16
+ const text = (v) => typeof v === "string" && v.length > 0 && v.length <= 256;
17
+ function numeric(v, keys, integer = false) {
18
+ if (!object(v) || !keys.every((k) => number(v[k]) && (!integer || Number.isSafeInteger(v[k]))))
19
+ return null;
20
+ return Object.fromEntries(keys.map((k) => [k, v[k]]));
21
+ }
22
+ export function parseSoakDiagnostics(body, status, expected) {
23
+ const fail = (status) => ({
24
+ status,
25
+ sample: null,
26
+ });
27
+ if (status === 404)
28
+ return fail("unsupported-endpoint");
29
+ if (status !== 200)
30
+ return fail("http-error");
31
+ if (body === null || body === "")
32
+ return fail("missing");
33
+ if (new TextEncoder().encode(body).length > DIAGNOSTICS_MAX_BYTES)
34
+ return fail("malformed");
35
+ let raw;
36
+ try {
37
+ raw = JSON.parse(body);
38
+ }
39
+ catch {
40
+ return fail("malformed");
41
+ }
42
+ if (!object(raw) || !object(raw.daemon))
43
+ return fail("malformed");
44
+ const d = raw.daemon;
45
+ if (!Number.isSafeInteger(d.protocolVersion) ||
46
+ !number(d.protocolVersion) ||
47
+ !text(d.productVersion) ||
48
+ !text(d.instanceId) ||
49
+ !text(d.startedAt) ||
50
+ !Number.isFinite(Date.parse(d.startedAt)) ||
51
+ (d.environmentId !== undefined && !text(d.environmentId)) ||
52
+ !Number.isSafeInteger(raw.pid) ||
53
+ !number(raw.pid) ||
54
+ raw.pid === 0)
55
+ return fail("malformed");
56
+ if (["protocolVersion", "productVersion", "instanceId", "startedAt", "environmentId"].some((k) => d[k] !== expected[k]) ||
57
+ raw.pid !== expected.pid)
58
+ return fail("identity-mismatch");
59
+ const memory = numeric(raw.memory, MEMORY_KEYS, true);
60
+ const cpu = numeric(raw.cpu, ["user", "system"], true);
61
+ const eventLoop = numeric(raw.eventLoop, ["idle", "active", "utilization"]);
62
+ const activeResources = raw.activeResources === null ? null : numeric(raw.activeResources, RESOURCE_KEYS, true);
63
+ if (!memory ||
64
+ !cpu ||
65
+ !eventLoop ||
66
+ eventLoop.utilization > 1 ||
67
+ !number(raw.uptimeMs) ||
68
+ !number(raw.sampledAtMs) ||
69
+ (raw.activeResources !== null && !activeResources))
70
+ return fail("malformed");
71
+ return {
72
+ status: "ok",
73
+ sample: {
74
+ daemon: {
75
+ protocolVersion: d.protocolVersion,
76
+ productVersion: d.productVersion,
77
+ instanceId: d.instanceId,
78
+ startedAt: d.startedAt,
79
+ ...(d.environmentId !== undefined ? { environmentId: d.environmentId } : {}),
80
+ },
81
+ pid: raw.pid,
82
+ uptimeMs: raw.uptimeMs,
83
+ sampledAtMs: raw.sampledAtMs,
84
+ memory,
85
+ cpu,
86
+ eventLoop,
87
+ activeResources,
88
+ },
89
+ };
90
+ }
91
+ /** Uptime is monotonic; wall-clock jumps do not change interval rates. */
92
+ export function diagnosticsDelta(previous, current) {
93
+ if (!previous)
94
+ return { status: "missing-baseline" };
95
+ if (previous.pid !== current.pid ||
96
+ ["protocolVersion", "productVersion", "instanceId", "startedAt", "environmentId"].some((key) => previous.daemon[key] !==
97
+ current.daemon[key]))
98
+ return { status: "identity-mismatch" };
99
+ const intervalMs = current.uptimeMs - previous.uptimeMs;
100
+ if (!Number.isFinite(intervalMs) || intervalMs <= 0)
101
+ return { status: "invalid-interval" };
102
+ const cpuUserMicros = current.cpu.user - previous.cpu.user;
103
+ const cpuSystemMicros = current.cpu.system - previous.cpu.system;
104
+ const eventLoopIdleMs = current.eventLoop.idle - previous.eventLoop.idle;
105
+ const eventLoopActiveMs = current.eventLoop.active - previous.eventLoop.active;
106
+ if (![cpuUserMicros, cpuSystemMicros, eventLoopIdleMs, eventLoopActiveMs].every(number))
107
+ return { status: "counter-regression" };
108
+ const cpuPercent = (cpuUserMicros + cpuSystemMicros) / (intervalMs * 10);
109
+ if (!Number.isFinite(cpuPercent))
110
+ return { status: "invalid-interval" };
111
+ const eventTotal = eventLoopIdleMs + eventLoopActiveMs;
112
+ if (!Number.isFinite(eventTotal))
113
+ return { status: "invalid-interval" };
114
+ return {
115
+ status: "ok",
116
+ intervalMs,
117
+ cpuUserMicros,
118
+ cpuSystemMicros,
119
+ cpuPercent,
120
+ eventLoopIdleMs,
121
+ eventLoopActiveMs,
122
+ eventLoopUtilization: eventTotal === 0 ? null : eventLoopActiveMs / eventTotal,
123
+ };
124
+ }