@wrongstack/cli 0.296.3 → 0.297.0

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 (83) hide show
  1. package/dist/boot/dispatch-webui.d.ts +7 -0
  2. package/dist/boot/dispatch-webui.d.ts.map +1 -1
  3. package/dist/boot/launch-banner.d.ts +22 -0
  4. package/dist/boot/launch-banner.d.ts.map +1 -0
  5. package/dist/boot/launch-menu.d.ts.map +1 -1
  6. package/dist/boot/tui-settings-adapter.d.ts.map +1 -1
  7. package/dist/{chunk-OI7GWJDS.js → chunk-7JABKXET.js} +372 -10
  8. package/dist/chunk-7JABKXET.js.map +7 -0
  9. package/dist/{chunk-WY7KKOUK.js → chunk-JW75HY4F.js} +31 -10
  10. package/dist/chunk-JW75HY4F.js.map +7 -0
  11. package/dist/{chunk-Z7YGASQP.js → chunk-QZKWRNIP.js} +19 -18
  12. package/dist/chunk-QZKWRNIP.js.map +7 -0
  13. package/dist/{chunk-BQCAJ7FI.js → chunk-XTIXVJXP.js} +16 -3
  14. package/dist/chunk-XTIXVJXP.js.map +7 -0
  15. package/dist/{cli-main-RTY35OPD.js → cli-main-GOY5YGVL.js} +25199 -25112
  16. package/dist/cli-main-GOY5YGVL.js.map +7 -0
  17. package/dist/cli-main-helpers.d.ts +1 -1
  18. package/dist/cli-main-helpers.d.ts.map +1 -1
  19. package/dist/cli-main.d.ts.map +1 -1
  20. package/dist/{execution-O7I5DYE6.js → execution-A4PGK4XV.js} +39 -9
  21. package/dist/execution-A4PGK4XV.js.map +7 -0
  22. package/dist/execution.d.ts.map +1 -1
  23. package/dist/fleet-statusline.d.ts.map +1 -1
  24. package/dist/goal-host.d.ts +1 -1
  25. package/dist/goal-host.d.ts.map +1 -1
  26. package/dist/{hq-JUYKUNJX.js → hq-GBPGXIR5.js} +2 -2
  27. package/dist/hq-publisher.d.ts +3 -2
  28. package/dist/hq-publisher.d.ts.map +1 -1
  29. package/dist/hq-server/types.d.ts +8 -0
  30. package/dist/hq-server/types.d.ts.map +1 -1
  31. package/dist/hq-server/ws.d.ts +61 -2
  32. package/dist/hq-server/ws.d.ts.map +1 -1
  33. package/dist/{hq-server-6AQ3BXZA.js → hq-server-YQUUMTPU.js} +2 -2
  34. package/dist/index.js +293 -26
  35. package/dist/index.js.map +4 -4
  36. package/dist/kanban-hq-sync.d.ts +13 -1
  37. package/dist/kanban-hq-sync.d.ts.map +1 -1
  38. package/dist/live-settings-input.d.ts +9 -0
  39. package/dist/live-settings-input.d.ts.map +1 -1
  40. package/dist/{mailbox-serve-2UWHEN3M.js → mailbox-serve-DBOOOOWZ.js} +50 -8
  41. package/dist/mailbox-serve-DBOOOOWZ.js.map +7 -0
  42. package/dist/{sdd-runtime-JG4EEKL6.js → sdd-runtime-AIJCJGWC.js} +2 -2
  43. package/dist/services/sdd/state.d.ts +3 -3
  44. package/dist/services/sdd/state.d.ts.map +1 -1
  45. package/dist/services/sdd/task-manager.d.ts.map +1 -1
  46. package/dist/session-stats.d.ts.map +1 -1
  47. package/dist/settings-menu.d.ts.map +1 -1
  48. package/dist/slash-commands/command-context.d.ts +2 -0
  49. package/dist/slash-commands/command-context.d.ts.map +1 -1
  50. package/dist/slash-commands/goal.d.ts +1 -1
  51. package/dist/slash-commands/goal.d.ts.map +1 -1
  52. package/dist/slash-commands/sdd.d.ts.map +1 -1
  53. package/dist/webui-server/kanban-host-adapter.d.ts +1 -1
  54. package/dist/webui-server/kanban-host-adapter.d.ts.map +1 -1
  55. package/dist/webui-server/session-start-payload.d.ts +4 -0
  56. package/dist/webui-server/session-start-payload.d.ts.map +1 -1
  57. package/dist/{webui-server-ZF5KVGC5.js → webui-server-XKFUOKMJ.js} +155 -132
  58. package/dist/webui-server-XKFUOKMJ.js.map +7 -0
  59. package/dist/webui-server-options.d.ts +7 -0
  60. package/dist/webui-server-options.d.ts.map +1 -1
  61. package/dist/webui-server.d.ts.map +1 -1
  62. package/dist/wiring/command-host-state.d.ts.map +1 -1
  63. package/dist/wiring/hq-telemetry.d.ts +4 -3
  64. package/dist/wiring/hq-telemetry.d.ts.map +1 -1
  65. package/dist/wiring/lifecycle-plugins.d.ts +5 -8
  66. package/dist/wiring/lifecycle-plugins.d.ts.map +1 -1
  67. package/dist/wiring/metrics.d.ts +1 -1
  68. package/dist/wiring/metrics.d.ts.map +1 -1
  69. package/dist/wiring/runtime-lifecycle-deps.d.ts.map +1 -1
  70. package/dist/wiring/sage.d.ts.map +1 -1
  71. package/dist/wiring/sdd-handlers.d.ts.map +1 -1
  72. package/package.json +22 -22
  73. package/dist/chunk-BQCAJ7FI.js.map +0 -7
  74. package/dist/chunk-OI7GWJDS.js.map +0 -7
  75. package/dist/chunk-WY7KKOUK.js.map +0 -7
  76. package/dist/chunk-Z7YGASQP.js.map +0 -7
  77. package/dist/cli-main-RTY35OPD.js.map +0 -7
  78. package/dist/execution-O7I5DYE6.js.map +0 -7
  79. package/dist/mailbox-serve-2UWHEN3M.js.map +0 -7
  80. package/dist/webui-server-ZF5KVGC5.js.map +0 -7
  81. /package/dist/{hq-JUYKUNJX.js.map → hq-GBPGXIR5.js.map} +0 -0
  82. /package/dist/{hq-server-6AQ3BXZA.js.map → hq-server-YQUUMTPU.js.map} +0 -0
  83. /package/dist/{sdd-runtime-JG4EEKL6.js.map → sdd-runtime-AIJCJGWC.js.map} +0 -0
@@ -72,6 +72,13 @@ export interface WebUIDispatchContext {
72
72
  getAllSessions(): import('@wrongstack/core/coordination').AgentVirtualSession[];
73
73
  loadSessionsFromDisk(): Promise<import('@wrongstack/core/coordination').AgentVirtualSession[]>;
74
74
  } | undefined;
75
+ /**
76
+ * Pre-computed update info from the CLI's preflight version check.
77
+ * When present, surfaces (WebUI, SimpleUI) receive appVersion,
78
+ * latestVersion, and updateAvailable fields in their session.start
79
+ * payload to display an "update available" warning.
80
+ */
81
+ updateInfo?: import('../webui-server/session-start-payload.js').BootUpdateInfo | undefined;
75
82
  /** Per-task agent factory for the SDD wizard's multi-agent run. */
76
83
  sddSubagentFactory?: import('@wrongstack/core/coordination').AgentFactory | undefined;
77
84
  onKanbanDispatch?: ((description: string, opts?: {
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch-webui.d.ts","sourceRoot":"","sources":["../../src/boot/dispatch-webui.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EACV,MAAM,EACN,UAAU,EACV,cAAc,EACd,SAAS,EACT,YAAY,EACZ,aAAa,EACb,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,QAAQ,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,+BAA+B,EAAE,0BAA0B,GAAG,SAAS,CAAC;IAC/F,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,uFAAuF;IACvF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IAChC,aAAa,EACT;QACE,WAAW,EAAE,OAAO,4BAA4B,EAAE,aAAa,CAAC;QAChE,IAAI,CAAC,EAAE,OAAO,+BAA+B,EAAE,mBAAmB,GAAG,SAAS,CAAC;QAC/E,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAClC,aAAa,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;KACtC,GACD,SAAS,CAAC;IACd,WAAW,EACP,CAAC,MAAM,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAC9E,SAAS,CAAC;IACd,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,4BAA4B,EAAE,YAAY,GAAG,SAAS,CAAC;IAC7E,yBAAyB,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3F,YAAY,EAAE,YAAY,GAAG,SAAS,CAAC;IACvC,WAAW,EAAE,UAAU,GAAG,SAAS,CAAC;IACpC,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,YAAY,EAAE,OAAO,wBAAwB,EAAE,YAAY,GAAG,SAAS,CAAC;IACxE,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,OAAO,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzE,sBAAsB,CAAC,EACnB,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC,GACnE,SAAS,CAAC;IACd,uBAAuB,CAAC,EACpB,CAAC,CACC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,OAAO,+BAA+B,EAAE,qBAAqB,KACnE,OAAO,CAAC,IAAI,CAAC,CAAC,GACnB,SAAS,CAAC;IACd,qBAAqB,CAAC,EAClB,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GACnE,SAAS,CAAC;IACd,4EAA4E;IAC5E,gBAAgB,CAAC,EACb;QACE,cAAc,IAAI,OAAO,+BAA+B,EAAE,mBAAmB,EAAE,CAAC;QAChF,oBAAoB,IAAI,OAAO,CAC7B,OAAO,+BAA+B,EAAE,mBAAmB,EAAE,CAC9D,CAAC;KACH,GACD,SAAS,CAAC;IACd,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,OAAO,+BAA+B,EAAE,YAAY,GAAG,SAAS,CAAC;IACtF,gBAAgB,CAAC,EACb,CAAC,CACC,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QACtC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACrC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC9B,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;QACpD,MAAM,CAAC,EACH,CAAC,CAAC,MAAM,EAAE;YACR,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;YAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;YAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;SAC5B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAC3B,SAAS,CAAC;KACf,KACE,OAAO,CAAC,MAAM,CAAC,CAAC,GACrB,SAAS,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiPjF"}
1
+ {"version":3,"file":"dispatch-webui.d.ts","sourceRoot":"","sources":["../../src/boot/dispatch-webui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pre-boot launch banner — the WRONGSTACK brand mark + wordmark rendered
3
+ * as raw ANSI strings for the plain-terminal launch menu.
4
+ *
5
+ * This is a deliberate plain-string twin of the TUI banner
6
+ * (packages/tui/src/components/history/banner.tsx). Importing the TUI
7
+ * component here would drag ink+react into the pre-boot path — which may
8
+ * end up launching WebUI/HQ and never need them. When the TUI glyph
9
+ * tables or palette change, update these in the same commit.
10
+ */
11
+ /** Exported so the menu can decide full vs compact by terminal width. */
12
+ export declare const WORDMARK_WIDTH: number;
13
+ /**
14
+ * Build the launch-screen banner as an array of pre-colored lines
15
+ * (no trailing newlines, no indent — the caller owns placement).
16
+ *
17
+ * Full art (mark + wordmark + tagline) when the terminal is wide enough;
18
+ * a one-line gradient fallback otherwise. Honors NO_COLOR / non-TTY by
19
+ * emitting plain text.
20
+ */
21
+ export declare function launchBannerLines(columns: number, version?: string): string[];
22
+ //# sourceMappingURL=launch-banner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-banner.d.ts","sourceRoot":"","sources":["../../src/boot/launch-banner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmDH,yEAAyE;AACzE,eAAO,MAAM,cAAc,QAAsD,CAAC;AAsElF;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"launch-menu.d.ts","sourceRoot":"","sources":["../../src/boot/launch-menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAEtD,yEAAyE;AACzE,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAKD,QAAA,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,CAItE,CAAC;AAsBF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACvC,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAuBT;AAuCD,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IACxC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,OAAO,aAAa,CAAC,GAAG,SAAS,CAAC;CAC1D;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA4D7F;AA6ID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,gBAAgB,GACvB,MAAM,EAAE,CA2BV;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,GACvB,gBAAgB,GAAG,SAAS,CAe9B;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
1
+ {"version":3,"file":"launch-menu.d.ts","sourceRoot":"","sources":["../../src/boot/launch-menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAI/D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGvD,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAEtD,yEAAyE;AACzE,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAKD,QAAA,MAAM,aAAa,EAAE,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,EAAE,MAAM,CAItE,CAAC;AAsBF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACvC,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAuBT;AAuCD,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IACxC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,OAAO,aAAa,CAAC,GAAG,SAAS,CAAC;CAC1D;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAyE7F;AAmVD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,gBAAgB,GACvB,MAAM,EAAE,CA2BV;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,GACvB,gBAAgB,GAAG,SAAS,CAe9B;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC,CA8Bf"}
@@ -1 +1 @@
1
- {"version":3,"file":"tui-settings-adapter.d.ts","sourceRoot":"","sources":["../../src/boot/tui-settings-adapter.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAU,WAAW,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AASnE,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,qBAAqB,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;IAClG,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACjE;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAChE;AAYD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,sBAAsB,GAAG,eAAe,CAselF"}
1
+ {"version":3,"file":"tui-settings-adapter.d.ts","sourceRoot":"","sources":["../../src/boot/tui-settings-adapter.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAU,WAAW,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AASnE,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,qBAAqB,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;IAClG,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACjE;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAChE;AAYD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,sBAAsB,GAAG,eAAe,CAsgBlF"}
@@ -18,8 +18,8 @@ import {
18
18
  import {
19
19
  createHqPersistence,
20
20
  HqAlertEngine,
21
- HqCommandAuditLog,
22
21
  HqBootstrapCodeStore,
22
+ HqCommandAuditLog,
23
23
  mintHqCookieSecret as mintHqCookieSecret2,
24
24
  mutateHqAuthFile as mutateHqAuthFile2,
25
25
  toAlertMessage,
@@ -2391,7 +2391,11 @@ function writeHqLanEndpoints(write, handle, browserToken) {
2391
2391
  // src/hq-server/ws.ts
2392
2392
  import { randomUUID as randomUUID3 } from "node:crypto";
2393
2393
  import {
2394
+ HQ_BROWSER_PEER_RESUME_CLIENT_ID,
2394
2395
  HQ_PROTOCOL_VERSION,
2396
+ HQ_RESUME_GAP_MAX_BYTES,
2397
+ HQ_RESUME_GAP_MAX_ENVELOPES,
2398
+ HQ_RESUME_GAP_MAX_STALE_MS,
2395
2399
  HQ_TRANSCRIPT_TEXT_CAP,
2396
2400
  parseHqEventPayload,
2397
2401
  parseHqFrame,
@@ -2402,7 +2406,274 @@ import {
2402
2406
  } from "@wrongstack/core/hq";
2403
2407
  import { WebSocket as WebSocket2 } from "ws";
2404
2408
  var MAX_EVENT_LOG = 5e3;
2405
- function handleBrowser(ws, snapshotBroadcaster, browsers) {
2409
+ function computeIsLeader(clients, projectId, acceptedCapabilities) {
2410
+ if (!acceptedCapabilities.includes("control.receive")) return false;
2411
+ for (const other of clients.values()) {
2412
+ if (other.projectId === projectId && other.capabilities.includes("control.receive")) {
2413
+ return false;
2414
+ }
2415
+ }
2416
+ return true;
2417
+ }
2418
+ function promoteNewLeader(clients, projectId, excludeWs, excludeClient) {
2419
+ const survivors = [];
2420
+ for (const [otherWs, otherClient] of clients) {
2421
+ if (otherWs === excludeWs || otherClient === excludeClient) continue;
2422
+ if (otherClient.projectId !== projectId) continue;
2423
+ if (!otherClient.capabilities.includes("control.receive")) continue;
2424
+ survivors.push(otherClient);
2425
+ }
2426
+ if (survivors.length === 0) return null;
2427
+ survivors.sort((a, b) => Date.parse(b.connectedAt) - Date.parse(a.connectedAt));
2428
+ const promoted = survivors[0];
2429
+ if (promoted) {
2430
+ promoted.isLeader = true;
2431
+ return promoted.clientId;
2432
+ }
2433
+ return null;
2434
+ }
2435
+ function detectLeaderLoss(lostClient, clients, browsers, reason, eventSink) {
2436
+ if (!lostClient.isLeader) return false;
2437
+ const detectedAt = (/* @__PURE__ */ new Date()).toISOString();
2438
+ const promotedLeaderId = promoteNewLeader(
2439
+ clients,
2440
+ lostClient.projectId,
2441
+ lostClient.ws,
2442
+ lostClient
2443
+ );
2444
+ const hasSurvivors = promotedLeaderId !== null;
2445
+ const machineId = lostClient.machineId?.trim() || lostClient.project.machineId.trim() || "unknown";
2446
+ const previousLeaderHandle = lostClient.clientId;
2447
+ const seq = nextPeerEventSeq(eventSink?.eventLog);
2448
+ let envelope;
2449
+ if (hasSurvivors) {
2450
+ if (checkPeerDedup(lostClient.projectId, previousLeaderHandle)) {
2451
+ return false;
2452
+ }
2453
+ const payload = {
2454
+ projectId: lostClient.projectId,
2455
+ machineId,
2456
+ leaderClientId: lostClient.clientId,
2457
+ previousLeaderHandle,
2458
+ reason,
2459
+ detectedAt
2460
+ };
2461
+ envelope = {
2462
+ id: `peer-${lostClient.clientId}-${Date.now()}`,
2463
+ type: "peer.rehydrate",
2464
+ schemaVersion: HQ_PROTOCOL_VERSION,
2465
+ timestamp: detectedAt,
2466
+ clientId: HQ_BROWSER_PEER_RESUME_CLIENT_ID,
2467
+ projectId: lostClient.projectId,
2468
+ seq,
2469
+ payload
2470
+ };
2471
+ recordPeerEmit(lostClient.projectId, previousLeaderHandle);
2472
+ } else {
2473
+ const payload = {
2474
+ projectId: lostClient.projectId,
2475
+ machineId,
2476
+ leaderClientId: lostClient.clientId,
2477
+ previousLeaderHandle,
2478
+ reason,
2479
+ detectedAt
2480
+ };
2481
+ envelope = {
2482
+ id: `peer-${lostClient.clientId}-${Date.now()}`,
2483
+ type: "peer.lost",
2484
+ schemaVersion: HQ_PROTOCOL_VERSION,
2485
+ timestamp: detectedAt,
2486
+ clientId: HQ_BROWSER_PEER_RESUME_CLIENT_ID,
2487
+ projectId: lostClient.projectId,
2488
+ seq,
2489
+ payload
2490
+ };
2491
+ }
2492
+ if (eventSink !== void 0) {
2493
+ eventSink.eventLog.push(envelope);
2494
+ if (eventSink.eventLog.length > MAX_EVENT_LOG) {
2495
+ eventSink.eventLog.splice(0, eventSink.eventLog.length - MAX_EVENT_LOG);
2496
+ }
2497
+ eventSink.persistence?.eventLog.append(envelope);
2498
+ }
2499
+ broadcastEvent(envelope, browsers);
2500
+ for (const [peerWs, peerClient] of clients) {
2501
+ if (peerWs === lostClient.ws) continue;
2502
+ if (peerClient.projectId !== lostClient.projectId) continue;
2503
+ sendGuarded(peerWs, JSON.stringify({ type: "hq.event", event: envelope }));
2504
+ }
2505
+ return true;
2506
+ }
2507
+ var peerEventSeq = 0;
2508
+ function nextPeerEventSeq(eventLog) {
2509
+ if (eventLog !== void 0) {
2510
+ for (const env of eventLog) {
2511
+ if (env.clientId === HQ_BROWSER_PEER_RESUME_CLIENT_ID && env.seq > peerEventSeq) {
2512
+ peerEventSeq = env.seq;
2513
+ }
2514
+ }
2515
+ }
2516
+ peerEventSeq += 1;
2517
+ return peerEventSeq;
2518
+ }
2519
+ function fanoutKanbanDelta(message, clients, browsers, projectId) {
2520
+ let clientsNotified = 0;
2521
+ for (const peer of clients.values()) {
2522
+ if (peer.projectId === projectId) {
2523
+ sendGuarded(peer.ws, message);
2524
+ clientsNotified++;
2525
+ }
2526
+ }
2527
+ let browsersNotified = 0;
2528
+ for (const browserWs of browsers) {
2529
+ sendGuarded(browserWs, message);
2530
+ browsersNotified++;
2531
+ }
2532
+ return { clientsNotified, browsersNotified };
2533
+ }
2534
+ function handleClientResume(ws, clients, eventLog, frame, snapshotBroadcaster) {
2535
+ const client = clients.get(ws);
2536
+ if (!client) return;
2537
+ if (frame.clientId !== void 0 && frame.clientId !== client.clientId) {
2538
+ ws.close(1008, "resume clientId mismatch");
2539
+ return;
2540
+ }
2541
+ if (client.kind !== "browser" && frame.projectId !== void 0 && frame.projectId !== client.projectId) {
2542
+ ws.close(1008, "resume projectId mismatch");
2543
+ return;
2544
+ }
2545
+ client.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
2546
+ const cursor = frame.lastSeqSeen;
2547
+ const now = Date.now();
2548
+ const recent = [];
2549
+ let cursorTimestamp;
2550
+ const skipPeerEnvelopes = client.clientId !== HQ_BROWSER_PEER_RESUME_CLIENT_ID;
2551
+ for (const env of eventLog) {
2552
+ if (env.clientId !== client.clientId) continue;
2553
+ if (skipPeerEnvelopes && (env.type === "peer.rehydrate" || env.type === "peer.lost")) {
2554
+ continue;
2555
+ }
2556
+ if (env.seq === cursor) cursorTimestamp = env.timestamp;
2557
+ if (env.seq > cursor) {
2558
+ recent.push(env);
2559
+ }
2560
+ }
2561
+ if (cursor > 0 && cursorTimestamp === void 0) {
2562
+ if (snapshotBroadcaster !== void 0) {
2563
+ sendGuarded(ws, snapshotBroadcaster.currentSerialized());
2564
+ return;
2565
+ }
2566
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
2567
+ sendGuarded(
2568
+ ws,
2569
+ JSON.stringify({
2570
+ type: "hq.resume_reject",
2571
+ reason: "log_unavailable",
2572
+ detectedAt: nowIso
2573
+ })
2574
+ );
2575
+ return;
2576
+ }
2577
+ const hasStale = cursorTimestamp !== void 0 && now - Date.parse(cursorTimestamp) > HQ_RESUME_GAP_MAX_STALE_MS;
2578
+ if (hasStale) {
2579
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
2580
+ sendGuarded(
2581
+ ws,
2582
+ JSON.stringify({
2583
+ type: "hq.resume_reject",
2584
+ reason: "last_seen_too_old",
2585
+ detectedAt: nowIso
2586
+ })
2587
+ );
2588
+ return;
2589
+ }
2590
+ let serializedByteLength = 0;
2591
+ for (const env of recent) {
2592
+ serializedByteLength += Buffer.byteLength(JSON.stringify(env), "utf8");
2593
+ if (serializedByteLength > HQ_RESUME_GAP_MAX_BYTES) break;
2594
+ }
2595
+ const gapIsTooLarge = recent.length > HQ_RESUME_GAP_MAX_ENVELOPES || serializedByteLength > HQ_RESUME_GAP_MAX_BYTES;
2596
+ if (gapIsTooLarge) {
2597
+ if (snapshotBroadcaster !== void 0) {
2598
+ sendGuarded(ws, snapshotBroadcaster.currentSerialized());
2599
+ return;
2600
+ }
2601
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
2602
+ sendGuarded(
2603
+ ws,
2604
+ JSON.stringify({
2605
+ type: "hq.resume_reject",
2606
+ reason: "gap_too_large",
2607
+ detectedAt: nowIso
2608
+ })
2609
+ );
2610
+ return;
2611
+ }
2612
+ recent.sort((a, b) => a.seq - b.seq);
2613
+ sendGuarded(
2614
+ ws,
2615
+ JSON.stringify({
2616
+ type: "hq.resume_gap",
2617
+ lastSeqSeen: cursor,
2618
+ envelopes: recent,
2619
+ truncated: false
2620
+ })
2621
+ );
2622
+ return;
2623
+ }
2624
+ var PEER_DEDUP_WINDOW_MS = 60 * 60 * 1e3;
2625
+ var peerDedupMap = /* @__PURE__ */ new Map();
2626
+ function peerDedupKey(projectId, previousLeaderHandle) {
2627
+ return `${projectId}::${previousLeaderHandle}`;
2628
+ }
2629
+ function checkPeerDedup(projectId, previousLeaderHandle) {
2630
+ const key = peerDedupKey(projectId, previousLeaderHandle);
2631
+ const now = Date.now();
2632
+ for (const [k, ts] of peerDedupMap) {
2633
+ if (now - ts > PEER_DEDUP_WINDOW_MS) peerDedupMap.delete(k);
2634
+ }
2635
+ const last = peerDedupMap.get(key);
2636
+ return last !== void 0 && now - last <= PEER_DEDUP_WINDOW_MS;
2637
+ }
2638
+ function recordPeerEmit(projectId, previousLeaderHandle) {
2639
+ peerDedupMap.set(peerDedupKey(projectId, previousLeaderHandle), Date.now());
2640
+ }
2641
+ function browserResumeClient(ws, clientId) {
2642
+ return /* @__PURE__ */ new Map([
2643
+ [
2644
+ ws,
2645
+ {
2646
+ ws,
2647
+ clientId,
2648
+ projectId: "",
2649
+ project: {
2650
+ projectId: "",
2651
+ projectName: "",
2652
+ projectRoot: "",
2653
+ machineId: "",
2654
+ workspaceKind: "git"
2655
+ },
2656
+ kind: "browser",
2657
+ connectedAt: (/* @__PURE__ */ new Date()).toISOString(),
2658
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString(),
2659
+ capabilities: [],
2660
+ declaredRedactionPolicy: {
2661
+ rawContent: false,
2662
+ toolArgs: "summary",
2663
+ paths: "project-relative"
2664
+ },
2665
+ lastEventSeq: 0,
2666
+ mailboxes: /* @__PURE__ */ new Map(),
2667
+ sessions: /* @__PURE__ */ new Map(),
2668
+ fleets: /* @__PURE__ */ new Map(),
2669
+ mcpSnapshots: /* @__PURE__ */ new Map(),
2670
+ commandQueue: [],
2671
+ isLeader: false
2672
+ }
2673
+ ]
2674
+ ]);
2675
+ }
2676
+ function handleBrowser(ws, snapshotBroadcaster, browsers, eventLog) {
2406
2677
  browsers.add(ws);
2407
2678
  ws.on("error", (err) => {
2408
2679
  console.warn(
@@ -2415,6 +2686,40 @@ function handleBrowser(ws, snapshotBroadcaster, browsers) {
2415
2686
  );
2416
2687
  });
2417
2688
  sendGuarded(ws, snapshotBroadcaster.currentSerialized());
2689
+ ws.on("message", (data) => {
2690
+ let raw;
2691
+ if (typeof data === "string") {
2692
+ raw = Buffer.from(data, "utf8");
2693
+ } else if (Buffer.isBuffer(data)) {
2694
+ raw = data;
2695
+ } else if (Array.isArray(data)) {
2696
+ raw = Buffer.concat(data);
2697
+ } else {
2698
+ raw = Buffer.from(new TextDecoder().decode(data), "utf8");
2699
+ }
2700
+ const parsed = parseHqFrame(raw);
2701
+ if (!parsed.ok) {
2702
+ const code = parsed.reason === "invalid-json" ? 1003 : 1008;
2703
+ ws.close(code, `invalid frame: ${parsed.reason}`);
2704
+ return;
2705
+ }
2706
+ const frame = parsed.frame;
2707
+ if (frame.type !== "client.resume") {
2708
+ ws.close(1008, "browser frame must be client.resume");
2709
+ return;
2710
+ }
2711
+ if (frame.clientId === void 0 || frame.clientId.length === 0) {
2712
+ ws.close(1008, "browser resume requires clientId");
2713
+ return;
2714
+ }
2715
+ handleClientResume(
2716
+ ws,
2717
+ browserResumeClient(ws, frame.clientId),
2718
+ eventLog,
2719
+ frame,
2720
+ snapshotBroadcaster
2721
+ );
2722
+ });
2418
2723
  ws.on("close", () => {
2419
2724
  browsers.delete(ws);
2420
2725
  });
@@ -2463,9 +2768,16 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
2463
2768
  (capability) => capability === "control.receive" || canPublishTelemetry
2464
2769
  );
2465
2770
  const declaredRedactionPolicy = resolveHqRedactionPolicy(payload.redactionPolicy);
2771
+ const supersededLeaders = [];
2772
+ let inheritedLeader = false;
2466
2773
  for (const [otherWs, otherClient] of clients) {
2774
+ const sameClientId = otherClient.clientId === payload.client.clientId;
2467
2775
  const samePublisher = otherClient.projectId === payload.project.projectId && otherClient.kind === payload.client.kind && otherClient.pid !== void 0 && payload.client.pid !== void 0 && otherClient.pid === payload.client.pid && (otherClient.machineId || otherClient.project.machineId) === (payload.client.machineId || payload.project.machineId);
2468
- if (otherWs !== ws && (otherClient.clientId === payload.client.clientId || samePublisher && otherClient.sessions.size === 0)) {
2776
+ if (otherWs !== ws && (sameClientId || samePublisher && otherClient.sessions.size === 0)) {
2777
+ if (otherClient.isLeader) {
2778
+ if (sameClientId) inheritedLeader = true;
2779
+ else supersededLeaders.push(otherClient);
2780
+ }
2469
2781
  clients.delete(otherWs);
2470
2782
  otherWs.close(4001, "superseded by a newer HQ connection");
2471
2783
  }
@@ -2490,10 +2802,19 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
2490
2802
  sessions: /* @__PURE__ */ new Map(),
2491
2803
  fleets: /* @__PURE__ */ new Map(),
2492
2804
  mcpSnapshots: /* @__PURE__ */ new Map(),
2493
- commandQueue: []
2805
+ commandQueue: [],
2806
+ // Leader for the project is the unique client with `control.receive`
2807
+ // capability. First such client wins; subsequent clients with the
2808
+ // capability are followers. Same-clientId reconnects inherit this
2809
+ // flag from the superseded socket because that identity never really
2810
+ // disappeared; different-client supersedes still use the loss detector.
2811
+ isLeader: inheritedLeader || computeIsLeader(clients, payload.project.projectId, acceptedCapabilities)
2494
2812
  };
2495
2813
  clients.set(ws, client);
2496
2814
  registered = true;
2815
+ for (const lostLeader of supersededLeaders) {
2816
+ detectLeaderLoss(lostLeader, clients, browsers, "crash", { eventLog, persistence });
2817
+ }
2497
2818
  const welcome = {
2498
2819
  type: "hq.welcome",
2499
2820
  protocolVersion: HQ_PROTOCOL_VERSION,
@@ -2583,6 +2904,10 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
2583
2904
  }
2584
2905
  return;
2585
2906
  }
2907
+ if (frame.type === "client.resume") {
2908
+ handleClientResume(ws, clients, eventLog, frame, snapshotBroadcaster);
2909
+ return;
2910
+ }
2586
2911
  if (frame.type === "client.event") {
2587
2912
  const client = clients.get(ws);
2588
2913
  if (!client) return;
@@ -2636,9 +2961,7 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
2636
2961
  tombstones: merged.tombstones.filter((record) => touched.has(record.boardId))
2637
2962
  };
2638
2963
  const message = JSON.stringify({ type: "hq.kanban_snapshot", payload: delta });
2639
- for (const peer of clients.values()) {
2640
- if (peer.projectId === client.projectId) sendGuarded(peer.ws, message);
2641
- }
2964
+ fanoutKanbanDelta(message, clients, browsers, client.projectId);
2642
2965
  }).catch((error) => {
2643
2966
  console.warn(
2644
2967
  JSON.stringify({
@@ -2768,7 +3091,11 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
2768
3091
  }
2769
3092
  });
2770
3093
  ws.on("close", () => {
3094
+ const lostClient = clients.get(ws);
2771
3095
  clients.delete(ws);
3096
+ if (lostClient) {
3097
+ detectLeaderLoss(lostClient, clients, browsers, "graceful", { eventLog, persistence });
3098
+ }
2772
3099
  snapshotBroadcaster.broadcast();
2773
3100
  });
2774
3101
  }
@@ -2837,6 +3164,9 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
2837
3164
  const transcripts = /* @__PURE__ */ new Map();
2838
3165
  const agentMessages = /* @__PURE__ */ new Map();
2839
3166
  const mailboxGateways = /* @__PURE__ */ new Map();
3167
+ const MAILBOX_GATEWAY_IDLE_TTL_MS = 15 * 6e4;
3168
+ const MAILBOX_GATEWAY_SWEEP_INTERVAL_MS = 6e4;
3169
+ const mailboxGatewayLastUsed = /* @__PURE__ */ new Map();
2840
3170
  const mailboxGatewayRateLimiter = new MailboxHttpRateLimiter();
2841
3171
  const mailboxGatewayRateLimitCleanup = setInterval(
2842
3172
  () => mailboxGatewayRateLimiter.cleanup(),
@@ -2924,7 +3254,10 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
2924
3254
  };
2925
3255
  const getMailboxGateway = (projectDir) => {
2926
3256
  const existing = mailboxGateways.get(projectDir);
2927
- if (existing) return existing;
3257
+ if (existing) {
3258
+ mailboxGatewayLastUsed.set(projectDir, Date.now());
3259
+ return existing;
3260
+ }
2928
3261
  const eventEmitter = new MailboxEventEmitter();
2929
3262
  const mailbox = createProjectMailbox({ projectDir, eventEmitter });
2930
3263
  const router = createMailboxHttpRouter({
@@ -2941,8 +3274,25 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
2941
3274
  });
2942
3275
  const gateway = { mailbox, router };
2943
3276
  mailboxGateways.set(projectDir, gateway);
3277
+ mailboxGatewayLastUsed.set(projectDir, Date.now());
2944
3278
  return gateway;
2945
3279
  };
3280
+ const evictIdleMailboxGateways = () => {
3281
+ const cutoff = Date.now() - MAILBOX_GATEWAY_IDLE_TTL_MS;
3282
+ for (const [projectDir, gateway] of [...mailboxGateways]) {
3283
+ if ((mailboxGatewayLastUsed.get(projectDir) ?? 0) > cutoff) continue;
3284
+ if (gateway.router.hasActiveStreams()) continue;
3285
+ mailboxGateways.delete(projectDir);
3286
+ mailboxGatewayLastUsed.delete(projectDir);
3287
+ gateway.router.close();
3288
+ void gateway.mailbox.close().catch(() => void 0);
3289
+ }
3290
+ };
3291
+ const mailboxGatewaySweepTimer = setInterval(
3292
+ evictIdleMailboxGateways,
3293
+ MAILBOX_GATEWAY_SWEEP_INTERVAL_MS
3294
+ );
3295
+ mailboxGatewaySweepTimer.unref?.();
2946
3296
  const alertEngine = new HqAlertEngine({
2947
3297
  onAlert: (alert) => {
2948
3298
  const msg = toAlertMessage(alert);
@@ -2989,8 +3339,13 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
2989
3339
  let changed = false;
2990
3340
  for (const [ws, client] of clients.entries()) {
2991
3341
  if (new Date(client.lastSeenAt).getTime() < cutoff) {
3342
+ const lostClient = client;
2992
3343
  ws.terminate();
2993
3344
  clients.delete(ws);
3345
+ detectLeaderLoss(lostClient, clients, browsers, "heartbeat-timeout", {
3346
+ eventLog,
3347
+ persistence
3348
+ });
2994
3349
  changed = true;
2995
3350
  continue;
2996
3351
  }
@@ -3010,8 +3365,13 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
3010
3365
  }
3011
3366
  }
3012
3367
  if (client.capabilities.includes("session.summary") && client.sessions.size === 0 && Date.now() - Date.parse(client.connectedAt) > sessionSnapshotTtlMs) {
3368
+ const lostClient = client;
3013
3369
  ws.terminate();
3014
3370
  clients.delete(ws);
3371
+ detectLeaderLoss(lostClient, clients, browsers, "heartbeat-timeout", {
3372
+ eventLog,
3373
+ persistence
3374
+ });
3015
3375
  changed = true;
3016
3376
  }
3017
3377
  }
@@ -3119,7 +3479,7 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
3119
3479
  });
3120
3480
  wss.on("connection", (ws, req, pathname) => {
3121
3481
  if (pathname === "/ws/browser") {
3122
- handleBrowser(ws, snapshotBroadcaster, browsers);
3482
+ handleBrowser(ws, snapshotBroadcaster, browsers, eventLog);
3123
3483
  } else {
3124
3484
  const token = new URL(req.url ?? "/", `http://${host}:${port}`).searchParams.get("token");
3125
3485
  handleClient(
@@ -3246,6 +3606,7 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
3246
3606
  clearInterval(browserHeartbeatTimer);
3247
3607
  clearInterval(timeseriesFlushTimer);
3248
3608
  clearInterval(mailboxGatewayRateLimitCleanup);
3609
+ clearInterval(mailboxGatewaySweepTimer);
3249
3610
  clearInterval(sessionCleanupTimer);
3250
3611
  stopAlertEngine();
3251
3612
  authWatcher.close();
@@ -3261,6 +3622,7 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
3261
3622
  ({ mailbox }) => mailbox.close().catch(() => void 0)
3262
3623
  );
3263
3624
  mailboxGateways.clear();
3625
+ mailboxGatewayLastUsed.clear();
3264
3626
  void Promise.all([
3265
3627
  ...mailboxCloses,
3266
3628
  persistence.eventLog.drain(),
@@ -3300,4 +3662,4 @@ export {
3300
3662
  startHqServer,
3301
3663
  HqInsecureExposureError2 as HqInsecureExposureError
3302
3664
  };
3303
- //# sourceMappingURL=chunk-OI7GWJDS.js.map
3665
+ //# sourceMappingURL=chunk-7JABKXET.js.map