local-operator-ui 0.31.26 → 0.31.28

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 (114) hide show
  1. package/out/main/index.js +383 -42
  2. package/out/preload/index.js +37 -0
  3. package/out/renderer/assets/{_basePickBy-D0wlCvva.js → _basePickBy-D16oopNi.js} +1 -1
  4. package/out/renderer/assets/{_baseUniq-DPa_8DrG.js → _baseUniq-BYKYfwtj.js} +1 -1
  5. package/out/renderer/assets/{agent-details-page-CrARDEtK.js → agent-details-page-CpwkLhdq.js} +2 -2
  6. package/out/renderer/assets/{agent-hub-page-tj8FYJ7M.js → agent-hub-page-BYeba0zC.js} +3 -3
  7. package/out/renderer/assets/agents-page-BQdR1fgV.js +7 -0
  8. package/out/renderer/assets/arc-BYg3WIzZ.js +1 -0
  9. package/out/renderer/assets/architectureDiagram-IEHRJDOE-np6HwkQz.js +36 -0
  10. package/out/renderer/assets/{blockDiagram-JOT3LUYC-D0DwK5Ey.js → blockDiagram-JOT3LUYC-CaR7YwVl.js} +2 -2
  11. package/out/renderer/assets/{book-open-DeQc_I6g.js → book-open--g1VlVMp.js} +1 -1
  12. package/out/renderer/assets/browser-page-BPwZPQY2.js +1 -0
  13. package/out/renderer/assets/browser-webauthn-prompt-Bsk0eXNZ.js +1 -0
  14. package/out/renderer/assets/{c4Diagram-VJAJSXHY-CubzqfYr.js → c4Diagram-VJAJSXHY-DqBT3rl5.js} +4 -4
  15. package/out/renderer/assets/channel-mWTr8anL.js +1 -0
  16. package/out/renderer/assets/{chunk-4BMEZGHF-AysrzODh.js → chunk-4BMEZGHF-B7-MzFPi.js} +1 -1
  17. package/out/renderer/assets/{chunk-A2AXSNBT-DfDo-OSy.js → chunk-A2AXSNBT-B9IqNrNg.js} +1 -1
  18. package/out/renderer/assets/{chunk-AEK57VVT-5xgHbFtp.js → chunk-AEK57VVT-DFzBJNd9.js} +1 -1
  19. package/out/renderer/assets/{chunk-D6G4REZN-pYCMpQiN.js → chunk-D6G4REZN-DW-XzsAv.js} +1 -1
  20. package/out/renderer/assets/{chunk-RZ5BOZE2-C5bWxzka.js → chunk-RZ5BOZE2-C7el6yre.js} +1 -1
  21. package/out/renderer/assets/{chunk-XZIHB7SX-B4wB3iEG.js → chunk-XZIHB7SX-BGYrq1Wn.js} +1 -1
  22. package/out/renderer/assets/classDiagram-GIVACNV2-DsNAZ0y6.js +1 -0
  23. package/out/renderer/assets/classDiagram-v2-COTLJTTW-DsNAZ0y6.js +1 -0
  24. package/out/renderer/assets/clone-48M_nlg0.js +1 -0
  25. package/out/renderer/assets/{console-mirror-45f9-7QH.js → console-mirror-ffbsozOd.js} +1 -1
  26. package/out/renderer/assets/{consoleCapture-GiahzrSM.js → consoleCapture-CiApa6fi.js} +1 -1
  27. package/out/renderer/assets/conversation-input-sync-CIGg7Rga.css +1 -0
  28. package/out/renderer/assets/conversation-input-sync-DQUYkOfh.js +911 -0
  29. package/out/renderer/assets/dagre-OKDRZEBW-BVScRDvj.js +4 -0
  30. package/out/renderer/assets/diagram-SSKATNLV-B0iQ6YY1.js +43 -0
  31. package/out/renderer/assets/diagram-VNBRO52H-Co6YPtUT.js +24 -0
  32. package/out/renderer/assets/{erDiagram-Q7BY3M3F-Cpuc-IVT.js → erDiagram-Q7BY3M3F-C9XIuSQl.js} +2 -2
  33. package/out/renderer/assets/error-boundary-DH4MhqhL.js +126 -0
  34. package/out/renderer/assets/{flowDiagram-4HSFHLVR-CsO4ubSn.js → flowDiagram-4HSFHLVR-QTHrGPk1.js} +2 -2
  35. package/out/renderer/assets/{ganttDiagram-APWFNJXF-LYPPAOmY.js → ganttDiagram-APWFNJXF-qC1VnBIz.js} +4 -4
  36. package/out/renderer/assets/gitGraphDiagram-7IBYFJ6S-D7UGP8uH.js +65 -0
  37. package/out/renderer/assets/{graph-uBeiISg2.js → graph-BIUuz2bp.js} +1 -1
  38. package/out/renderer/assets/index-C5Ighs_C.css +1 -0
  39. package/out/renderer/assets/index-Ckvy9Zge.js +1 -0
  40. package/out/renderer/assets/{index-Dn3kYWFt.js → index-D-TXozHA.js} +9 -9
  41. package/out/renderer/assets/index-DHKnfVbX.css +1 -0
  42. package/out/renderer/assets/index-DTc-sJ8S.js +7 -0
  43. package/out/renderer/assets/index-vK67PDia.js +784 -0
  44. package/out/renderer/assets/infoDiagram-PH2N3AL5-DYtVGJC7.js +2 -0
  45. package/out/renderer/assets/installer-BcSiJA6N.js +1 -0
  46. package/out/renderer/assets/{journeyDiagram-U35MCT3I-B6AH0Dcn.js → journeyDiagram-U35MCT3I-vVg2sFTp.js} +6 -6
  47. package/out/renderer/assets/{kanban-definition-NDS4AKOZ-C7dciPrH.js → kanban-definition-NDS4AKOZ-BE36wSk2.js} +4 -4
  48. package/out/renderer/assets/{layout-hxpgEDIm.js → layout-bOQrw0b8.js} +1 -1
  49. package/out/renderer/assets/{legacy-agents-page-C6kd3fjG.js → legacy-agents-page-BSKz_UrV.js} +5 -5
  50. package/out/renderer/assets/{mermaid.core-CC8CflzC.js → mermaid.core-DpvY6Bhk.js} +5 -5
  51. package/out/renderer/assets/mesh-page-9Q8Sc9J_.js +1 -0
  52. package/out/renderer/assets/mindmap-definition-ALO5MXBD-B9PPN6sb.js +95 -0
  53. package/out/renderer/assets/mini-ySULdryU.js +1 -0
  54. package/out/renderer/assets/org-surface-gate-Dl4zcl5q.js +1 -0
  55. package/out/renderer/assets/{page-header-Dt6h8jvu.js → page-header-DazB-GQz.js} +1 -1
  56. package/out/renderer/assets/pieDiagram-IB7DONF6-DkioNhQY.js +30 -0
  57. package/out/renderer/assets/projects-page-C37RiUJz.js +35 -0
  58. package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-bRmQaOWY.js → quadrantDiagram-7GDLP6J5-4m3m0N6F.js} +4 -4
  59. package/out/renderer/assets/{radar-MK3ICKWK-B1I3333M.js → radar-MK3ICKWK-DS8YGAOb.js} +1 -1
  60. package/out/renderer/assets/radient-auth-buttons-DZeHRL7u.js +1 -0
  61. package/out/renderer/assets/{requirementDiagram-KVF5MWMF-B29w6CHz.js → requirementDiagram-KVF5MWMF-DsBdUu3C.js} +3 -3
  62. package/out/renderer/assets/sankeyDiagram-QLVOVGJD-CnaPYKR6.js +10 -0
  63. package/out/renderer/assets/{schedules-page-CXz63fN5.js → schedules-page-B0dSGpXt.js} +12 -12
  64. package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-D7gyUvnw.js → sequenceDiagram-X6HHIX6F-C3eja1QY.js} +2 -2
  65. package/out/renderer/assets/settings-page-CrMsVX3d.js +292 -0
  66. package/out/renderer/assets/{settings-section-DMXSpP_D.js → settings-section-BGG4XzV0.js} +1 -1
  67. package/out/renderer/assets/stateDiagram-DGXRK772-CjFKiyXn.js +1 -0
  68. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-BsbYpgkp.js +1 -0
  69. package/out/renderer/assets/{timeline-definition-BDJGKUSR-MePOCWzB.js → timeline-definition-BDJGKUSR-CAXy0nQD.js} +2 -2
  70. package/out/renderer/assets/ui-preferences-store-B6Fr0UX9.js +1 -0
  71. package/out/renderer/assets/{use-download-agent-mutation-BNAfnIa9.js → use-download-agent-mutation-LsNre_-6.js} +2 -2
  72. package/out/renderer/assets/use-memberships-query-BgiSfYgr.js +1 -0
  73. package/out/renderer/assets/{xychartDiagram-VJFVF3MP-heZP1JY-.js → xychartDiagram-VJFVF3MP-CfNm8SWG.js} +6 -6
  74. package/out/renderer/console-capture.html +5 -5
  75. package/out/renderer/index.html +9 -9
  76. package/out/renderer/installer.html +4 -5
  77. package/out/renderer/mini.html +7 -6
  78. package/package.json +3 -4
  79. package/out/renderer/assets/agents-page-CbQN_5wE.js +0 -7
  80. package/out/renderer/assets/aida-control-BuMafqCb.js +0 -50
  81. package/out/renderer/assets/arc-BG4bHuQI.js +0 -1
  82. package/out/renderer/assets/architectureDiagram-IEHRJDOE-CkYkkIY9.js +0 -36
  83. package/out/renderer/assets/browser-page-CIhGr6vl.js +0 -1
  84. package/out/renderer/assets/browser-webauthn-prompt-CYou_6QY.js +0 -1
  85. package/out/renderer/assets/channel-B-eIVFbM.js +0 -1
  86. package/out/renderer/assets/classDiagram-GIVACNV2-BN6aG5Qf.js +0 -1
  87. package/out/renderer/assets/classDiagram-v2-COTLJTTW-BN6aG5Qf.js +0 -1
  88. package/out/renderer/assets/clone-C3CHGd7j.js +0 -1
  89. package/out/renderer/assets/dagre-OKDRZEBW-CBRdPb8k.js +0 -4
  90. package/out/renderer/assets/diagram-SSKATNLV-DyGZgIL1.js +0 -43
  91. package/out/renderer/assets/diagram-VNBRO52H-Con45RzE.js +0 -24
  92. package/out/renderer/assets/error-boundary-CdtTgj_c.js +0 -6
  93. package/out/renderer/assets/gitGraphDiagram-7IBYFJ6S-BO_dix1Z.js +0 -65
  94. package/out/renderer/assets/index-2qmFWi_F.css +0 -1
  95. package/out/renderer/assets/index-Bi4c5ZZH.js +0 -7
  96. package/out/renderer/assets/index-CIj3B-eB.js +0 -1
  97. package/out/renderer/assets/index-COUHPq03.js +0 -1533
  98. package/out/renderer/assets/index-D8ujEXbz.css +0 -1
  99. package/out/renderer/assets/infoDiagram-PH2N3AL5-BqRYyG2a.js +0 -2
  100. package/out/renderer/assets/installer-zpOWwoWQ.js +0 -1
  101. package/out/renderer/assets/mesh-page-M4Y20e0t.js +0 -2
  102. package/out/renderer/assets/mindmap-definition-ALO5MXBD-BWI1IBWM.js +0 -95
  103. package/out/renderer/assets/mini-Dtj-bCUA.js +0 -1
  104. package/out/renderer/assets/org-surface-gate-CjPxDSXP.js +0 -1
  105. package/out/renderer/assets/pieDiagram-IB7DONF6-DNymJR3P.js +0 -30
  106. package/out/renderer/assets/projects-page-C3N6NYIX.js +0 -35
  107. package/out/renderer/assets/radient-auth-buttons-CCXHTNWU.js +0 -1
  108. package/out/renderer/assets/sankeyDiagram-QLVOVGJD-BNv_qf23.js +0 -10
  109. package/out/renderer/assets/settings-page-CsCf63si.js +0 -292
  110. package/out/renderer/assets/stateDiagram-DGXRK772-Dd3BswSO.js +0 -1
  111. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-D9cOpOSm.js +0 -1
  112. package/out/renderer/assets/textarea-DsHM-W2p.js +0 -121
  113. package/out/renderer/assets/ui-preferences-store-BmAT9G_p.js +0 -1
  114. package/out/renderer/assets/use-memberships-query-PWqngUAl.js +0 -1
package/out/main/index.js CHANGED
@@ -62,11 +62,14 @@ function pairingHasRemedy(cause) {
62
62
  const id$1 = zod.z.string().min(1).max(128).regex(/^[a-zA-Z0-9_-]+$/);
63
63
  const settingKey = zod.z.string().min(1).max(256).regex(/^[a-zA-Z0-9_][a-zA-Z0-9_.-]*$/);
64
64
  const secret = zod.z.string().min(1).max(32768);
65
- const sessionId = zod.z.string().regex(/^[a-f0-9]{12}$/);
65
+ const sessionIdPattern = /^[a-f0-9]{12}$/;
66
+ const sessionId = zod.z.string().regex(sessionIdPattern);
67
+ const skillCwd = zod.z.string().min(1).max(4096);
66
68
  const mcpServerName = zod.z.string().regex(/^[A-Za-z0-9_.:-]{1,100}$/);
67
69
  const mcpSecretReference = zod.z.string().regex(/^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/);
68
70
  const mcpOperationId = zod.z.string().regex(/^[a-f0-9]{32}$/);
69
- const mcpCatalogCwd = zod.z.string().min(1).max(4096).regex(/^(\/|[A-Za-z]:[\\/])/);
71
+ const mcpCatalogCwdPattern = /^(\/|[A-Za-z]:[\\/])/;
72
+ const mcpCatalogCwd = zod.z.string().min(1).max(4096).regex(mcpCatalogCwdPattern);
70
73
  const SUBSCRIPTION_ID_PATTERN = /^[a-f0-9]{32}$/;
71
74
  const requestId = zod.z.string().regex(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/);
72
75
  const meshId = zod.z.string().regex(/^[A-Za-z0-9_-]{1,128}$/);
@@ -262,6 +265,7 @@ const projectKey = zod.z.string().min(1).max(64);
262
265
  const PROJECT_PROGRESS_MAX_CHARS = 1e3;
263
266
  const PROJECT_DESCRIPTION_MAX_CHARS = 240;
264
267
  const PROJECT_MILESTONE_NAME_MAX_CHARS = 80;
268
+ const hubItemKind = zod.z.enum(["agent", "team"]);
265
269
  const desktopRequestUnion = zod.z.discriminatedUnion("op", [
266
270
  zod.z.object({ op: zod.z.literal("capabilities") }).strict(),
267
271
  zod.z.object({ op: zod.z.literal("profiles.list") }).strict(),
@@ -282,6 +286,47 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
282
286
  requestId,
283
287
  fields: profileFields
284
288
  }).strict(),
289
+ /*
290
+ * THE HUB AUTO-UPDATE PLANE (backend `server/routes/desktop_hub.py`, gated by
291
+ * the `hub_updates` capability). `hub.updates` is a STORE READ on the backend
292
+ * (no network, O(items)), which is what makes it safe to poll from the
293
+ * sidebar; the five mutations are the ones that touch the hub or write a
294
+ * local definition, and each carries a `requestId` so a lost response is
295
+ * replayed by the server's receipts rather than re-run.
296
+ *
297
+ * `kind` is closed to the two definition families the hub carries, and `name`
298
+ * is the profile/team NAME because names are the runtime's attachment keys.
299
+ * `prefer` is per-item only: the backend refuses it on apply-all, since a
300
+ * conflict decision cannot be made for a set of items at once, so the
301
+ * apply-all schema does not offer it.
302
+ */
303
+ zod.z.object({ op: zod.z.literal("hub.updates") }).strict(),
304
+ zod.z.object({
305
+ op: zod.z.literal("hub.check"),
306
+ requestId,
307
+ kind: hubItemKind.optional(),
308
+ name: profileName.optional()
309
+ }).strict(),
310
+ zod.z.object({
311
+ op: zod.z.literal("hub.apply"),
312
+ requestId,
313
+ kind: hubItemKind,
314
+ name: profileName,
315
+ prefer: zod.z.enum(["local", "remote"]).optional(),
316
+ acknowledgeUnknownBaseline: zod.z.boolean().optional(),
317
+ dryRun: zod.z.boolean().optional()
318
+ }).strict(),
319
+ zod.z.object({
320
+ op: zod.z.literal("hub.applyAll"),
321
+ requestId,
322
+ kind: hubItemKind.optional()
323
+ }).strict(),
324
+ zod.z.object({
325
+ op: zod.z.literal("hub.retry"),
326
+ requestId,
327
+ kind: hubItemKind,
328
+ name: profileName
329
+ }).strict(),
285
330
  zod.z.object({ op: zod.z.literal("teams.list") }).strict(),
286
331
  zod.z.object({ op: zod.z.literal("teams.get"), name: profileName }).strict(),
287
332
  zod.z.object({
@@ -1163,7 +1208,20 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
1163
1208
  */
1164
1209
  recentLimit: zod.z.number().int().min(0).max(50).optional()
1165
1210
  }).strict(),
1166
- zod.z.object({ op: zod.z.literal("skills.list"), sessionId, name: id$1.optional() }).strict(),
1211
+ zod.z.object({
1212
+ op: zod.z.literal("skills.list"),
1213
+ /*
1214
+ * The sessionless arm's pair, both optional, `cwd` wins when both are sent
1215
+ * (the explicit folder beats the implied one; the route's docstring owns
1216
+ * the rule). `cwd` given → the folder's own discovery roots (identical to
1217
+ * the session created there); `session_id` given → the session's cwd
1218
+ * (compat, for the `/skills` panel and released clients); NEITHER → the
1219
+ * daemon's home roots, explicitly — never the daemon's process cwd.
1220
+ */
1221
+ sessionId: sessionId.optional(),
1222
+ cwd: skillCwd.optional(),
1223
+ name: id$1.optional()
1224
+ }).strict(),
1167
1225
  zod.z.object({ op: zod.z.literal("sessions.failovers"), sessionId }).strict(),
1168
1226
  zod.z.object({
1169
1227
  op: zod.z.literal("sessions.credential"),
@@ -1860,11 +1918,36 @@ function moveClientBoundMs(shape) {
1860
1918
  const waitS = Math.max(0, shape.waitS ?? 0);
1861
1919
  return (MOVE_OP_DEADLINE_S + hold + MOVE_CONTROL_SLACK_S + MOVE_CLIENT_MARGIN_S) * 1e3 + waitS * 1e3;
1862
1920
  }
1921
+ const HUB_ITEM_MERGE_S = 120;
1922
+ const HUB_ITEM_WRITE_DEADLINE_MS = (HUB_ITEM_MERGE_S + 30 + MOVE_CLIENT_MARGIN_S) * 1e3;
1923
+ const HUB_CHECK_DEADLINE_MS = 6e4;
1924
+ const HUB_APPLY_ALL_DEADLINE_MS = 5 * HUB_ITEM_WRITE_DEADLINE_MS;
1925
+ const HUB_WRITE_OPS = /* @__PURE__ */ new Set([
1926
+ "hub.apply",
1927
+ "hub.retry",
1928
+ "hub.check",
1929
+ "hub.applyAll"
1930
+ ]);
1931
+ function hubWriteDeadlineMs(op) {
1932
+ switch (op) {
1933
+ case "hub.apply":
1934
+ case "hub.retry":
1935
+ return HUB_ITEM_WRITE_DEADLINE_MS;
1936
+ case "hub.check":
1937
+ return HUB_CHECK_DEADLINE_MS;
1938
+ case "hub.applyAll":
1939
+ return HUB_APPLY_ALL_DEADLINE_MS;
1940
+ default:
1941
+ return null;
1942
+ }
1943
+ }
1863
1944
  function desktopRequestDeadlineMs(request) {
1864
1945
  if (typeof request !== "string" && request.op === "sessions.transfer") {
1865
1946
  return moveClientBoundMs(request) + MOVE_APP_MARGIN_MS;
1866
1947
  }
1867
1948
  const op = typeof request === "string" ? request : request.op;
1949
+ const hub = hubWriteDeadlineMs(op);
1950
+ if (hub !== null) return hub;
1868
1951
  return LONG_READ_OPS.has(op) ? DESKTOP_LONG_READ_DEADLINE_MS : DESKTOP_CONTROL_DEADLINE_MS;
1869
1952
  }
1870
1953
  const DESKTOP_DEADLINE_EXCEEDED_CODE = "deadline_exceeded";
@@ -1878,6 +1961,7 @@ const READ_ONLY_OPS = /* @__PURE__ */ new Set([
1878
1961
  "commands.list",
1879
1962
  "config.get",
1880
1963
  "credentials.list",
1964
+ "hub.updates",
1881
1965
  "info.get",
1882
1966
  "instructions.get",
1883
1967
  "legacy.agent.get",
@@ -1934,6 +2018,12 @@ function desktopRequestDeadlineDetail(op, deadlineMs) {
1934
2018
  message: `The app waits up to ${seconds} seconds for a move, and it was still running when the app stopped waiting. The move was asked for, so its outcome is unknown from here: read the session again before moving it anywhere else.`
1935
2019
  };
1936
2020
  }
2021
+ if (HUB_WRITE_OPS.has(op)) {
2022
+ return {
2023
+ code,
2024
+ message: "This is taking longer than expected. It may still finish; the mark will update."
2025
+ };
2026
+ }
1937
2027
  if (READ_ONLY_OPS.has(op)) {
1938
2028
  return {
1939
2029
  code,
@@ -2062,6 +2152,52 @@ function desktopEndpoint(request) {
2062
2152
  method: "PATCH",
2063
2153
  body: { request_id: request.requestId, ...request.fields }
2064
2154
  };
2155
+ case "hub.updates":
2156
+ return { path: "/v1/desktop/hub/updates", method: "GET" };
2157
+ case "hub.check":
2158
+ return {
2159
+ path: "/v1/desktop/hub/updates/check",
2160
+ method: "POST",
2161
+ body: {
2162
+ request_id: request.requestId,
2163
+ ...request.kind ? { kind: request.kind } : {},
2164
+ ...request.name ? { name: request.name } : {}
2165
+ }
2166
+ };
2167
+ case "hub.apply":
2168
+ return {
2169
+ path: "/v1/desktop/hub/updates/apply",
2170
+ method: "POST",
2171
+ body: {
2172
+ request_id: request.requestId,
2173
+ kind: request.kind,
2174
+ name: request.name,
2175
+ ...request.prefer ? { prefer: request.prefer } : {},
2176
+ ...request.acknowledgeUnknownBaseline !== void 0 ? {
2177
+ acknowledge_unknown_baseline: request.acknowledgeUnknownBaseline
2178
+ } : {},
2179
+ ...request.dryRun !== void 0 ? { dry_run: request.dryRun } : {}
2180
+ }
2181
+ };
2182
+ case "hub.applyAll":
2183
+ return {
2184
+ path: "/v1/desktop/hub/updates/apply-all",
2185
+ method: "POST",
2186
+ body: {
2187
+ request_id: request.requestId,
2188
+ ...request.kind ? { kind: request.kind } : {}
2189
+ }
2190
+ };
2191
+ case "hub.retry":
2192
+ return {
2193
+ path: "/v1/desktop/hub/updates/retry",
2194
+ method: "POST",
2195
+ body: {
2196
+ request_id: request.requestId,
2197
+ kind: request.kind,
2198
+ name: request.name
2199
+ }
2200
+ };
2065
2201
  case "teams.list":
2066
2202
  return { path: "/v1/desktop/teams", method: "GET" };
2067
2203
  case "teams.get":
@@ -2684,11 +2820,17 @@ function desktopEndpoint(request) {
2684
2820
  method: "GET"
2685
2821
  };
2686
2822
  }
2687
- case "skills.list":
2823
+ case "skills.list": {
2824
+ const query = new URLSearchParams();
2825
+ if (request.sessionId) query.set("session_id", request.sessionId);
2826
+ if (request.cwd) query.set("cwd", request.cwd);
2827
+ if (request.name) query.set("name", request.name);
2828
+ const encoded = query.toString();
2688
2829
  return {
2689
- path: `/v1/desktop/skills?session_id=${request.sessionId}${request.name ? `&name=${encodeURIComponent(request.name)}` : ""}`,
2830
+ path: `/v1/desktop/skills${encoded ? `?${encoded}` : ""}`,
2690
2831
  method: "GET"
2691
2832
  };
2833
+ }
2692
2834
  case "sessions.failovers":
2693
2835
  return {
2694
2836
  path: `/v1/desktop/sessions/${request.sessionId}/failovers`,
@@ -2968,6 +3110,9 @@ const MINI_VIEW_SUMMONED = "mini-view:summoned";
2968
3110
  const MINI_VIEW_DISMISS = "mini-view:dismiss";
2969
3111
  const MINI_VIEW_REGISTRATION = "mini-view:registration";
2970
3112
  const MINI_VIEW_REGISTRATION_GET = "mini-view:registration-get";
3113
+ const MINI_VIEW_RESIZE = "mini-view:resize";
3114
+ const MINI_VIEW_DIALOG = "mini-view:dialog";
3115
+ const MINI_VIEW_PAINTED = "mini-view:painted";
2971
3116
  const MINI_VIEW_DISMISS_REASONS = [
2972
3117
  "escape",
2973
3118
  "blur",
@@ -2979,6 +3124,10 @@ function isMiniViewDismissReason(value) {
2979
3124
  }
2980
3125
  const MINI_VIEW_WIDTH = 640;
2981
3126
  const MINI_VIEW_HEIGHT = 168;
3127
+ const MINI_VIEW_MAX_HEIGHT = 560;
3128
+ function isMiniViewResizePayload(value) {
3129
+ return value !== null && typeof value === "object" && typeof value.height === "number" && Number.isFinite(value.height);
3130
+ }
2982
3131
  const DEFAULT_QUICK_SEND_VALUE = "primary+alt+shift+space";
2983
3132
  const FUNCTION_KEY_TOKEN = /^f([1-9]|1[0-9]|2[0-4])$/;
2984
3133
  function isFunctionKeyToken(token) {
@@ -33106,17 +33255,43 @@ const MINI_DOCUMENT = "mini.html";
33106
33255
  function miniViewUrlFor(rendererUrl) {
33107
33256
  return INDEX_DOCUMENT_SUFFIX.test(rendererUrl) ? rendererUrl.replace(INDEX_DOCUMENT_SUFFIX, `/${MINI_DOCUMENT}`) : `${rendererUrl.replace(TRAILING_SLASH, "")}/${MINI_DOCUMENT}`;
33108
33257
  }
33258
+ const MINI_VIEW_GROUND = "#22201c";
33259
+ const MINI_VIEW_PAINT_TIMEOUT_MS = 1500;
33260
+ function miniViewPresentationFor(input) {
33261
+ if (input.show !== "focus") return "present";
33262
+ if (input.painted) return "present";
33263
+ if (input.timedOut) return "error-surface";
33264
+ return "wait";
33265
+ }
33266
+ function miniViewErrorSurfaceHtml() {
33267
+ return `<!doctype html>
33268
+ <html><head><meta charset="utf-8" /><title>Quick send</title>
33269
+ <style>
33270
+ :root { color-scheme: dark; }
33271
+ html, body { margin: 0; height: 100%; background: ${MINI_VIEW_GROUND}; }
33272
+ body { display: flex; align-items: center; padding: 0 16px;
33273
+ font: 13px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
33274
+ color: #c2bcb0; }
33275
+ p { margin: 0; }
33276
+ strong { font-weight: 600; color: #ef8078; }
33277
+ </style></head>
33278
+ <body><p><strong>Quick send could not open.</strong> Try the hotkey again, or restart Local Operator if it keeps failing.</p></body></html>`;
33279
+ }
33109
33280
  function createMiniView(options) {
33110
33281
  const window = new electron.BrowserWindow({
33111
33282
  width: MINI_VIEW_WIDTH,
33112
33283
  height: MINI_VIEW_HEIGHT,
33113
33284
  /*
33114
- * Sized in content, not window, terms: the design fixes the COMPOSER at
33115
- * 640x168 and the frames are captured and reviewed at that size, so the
33116
- * page's viewport is the number that must be exact on every platform.
33285
+ * Sized in content, not window, terms, and the content size is now the
33286
+ * BASE of a measured range rather than the whole story: the restyle's
33287
+ * chrome (previews, the readings strip, the picker sheet) asks for more
33288
+ * through `MINI_VIEW_RESIZE` below, and this call is what makes the
33289
+ * page's viewport the number every frame is captured at.
33117
33290
  */
33118
33291
  useContentSize: true,
33119
33292
  show: false,
33293
+ /* The dark ground under an as-yet-unpainted document; see MINI_VIEW_GROUND. */
33294
+ backgroundColor: MINI_VIEW_GROUND,
33120
33295
  // A composer that floats over other apps carries no frame of its own:
33121
33296
  // the app's own rows are the entire surface, and the one sanctioned
33122
33297
  // elevation step is the card's ground.
@@ -33164,7 +33339,91 @@ function createMiniView(options) {
33164
33339
  if (process.platform === "darwin") {
33165
33340
  window.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
33166
33341
  }
33342
+ let paintedForLoad = false;
33343
+ let paintTimer = null;
33344
+ let paintWaiters = [];
33345
+ const settlePaint = (ready) => {
33346
+ if (paintTimer !== null) {
33347
+ clearTimeout(paintTimer);
33348
+ paintTimer = null;
33349
+ }
33350
+ paintedForLoad = ready;
33351
+ const waiting = paintWaiters;
33352
+ paintWaiters = [];
33353
+ for (const resolve of waiting) resolve();
33354
+ };
33355
+ const showErrorSurface = () => {
33356
+ if (disposed || window.isDestroyed()) return;
33357
+ settlePaint(false);
33358
+ void window.loadURL(
33359
+ `data:text/html;charset=utf-8,${encodeURIComponent(miniViewErrorSurfaceHtml())}`
33360
+ );
33361
+ window.webContents.once("did-finish-load", () => {
33362
+ if (disposed || window.isDestroyed()) return;
33363
+ positionForCursorDisplay();
33364
+ presentMiniView(window, options.show, {
33365
+ trigger: "mini-view",
33366
+ report: options.report
33367
+ });
33368
+ if (options.show === "focus") {
33369
+ window.webContents.send(MINI_VIEW_SUMMONED, {
33370
+ at: Date.now()
33371
+ });
33372
+ }
33373
+ });
33374
+ };
33375
+ window.webContents.on("did-start-loading", () => {
33376
+ paintedForLoad = false;
33377
+ });
33378
+ window.webContents.on(
33379
+ "did-fail-load",
33380
+ (_event, errorCode, errorDescription, validatedURL, isMainFrame) => {
33381
+ if (!isMainFrame) return;
33382
+ options.report?.(
33383
+ `[mini-view] load failed (${errorCode} ${errorDescription}) for ${validatedURL}; showing the error card`
33384
+ );
33385
+ showErrorSurface();
33386
+ }
33387
+ );
33388
+ window.webContents.on("render-process-gone", (_event, details) => {
33389
+ options.report?.(
33390
+ `[mini-view] renderer gone (${details.reason}); showing the error card`
33391
+ );
33392
+ showErrorSurface();
33393
+ });
33167
33394
  void window.loadURL(options.url);
33395
+ const onResize = (event, payload) => {
33396
+ if (window.isDestroyed() || event.sender !== window.webContents || event.senderFrame !== window.webContents.mainFrame) {
33397
+ return;
33398
+ }
33399
+ if (!isMiniViewResizePayload(payload)) return;
33400
+ const area = electron.screen.getDisplayMatching(window.getBounds()).workArea;
33401
+ const ceiling = Math.max(
33402
+ MINI_VIEW_HEIGHT,
33403
+ Math.min(MINI_VIEW_MAX_HEIGHT, area.height - 64)
33404
+ );
33405
+ const height = Math.max(
33406
+ MINI_VIEW_HEIGHT,
33407
+ Math.min(Math.round(payload.height), ceiling)
33408
+ );
33409
+ const bounds = window.getContentBounds();
33410
+ if (bounds.height === height) return;
33411
+ window.setContentSize(MINI_VIEW_WIDTH, height);
33412
+ const bottom = bounds.y + height;
33413
+ if (bottom > area.y + area.height) {
33414
+ window.setPosition(
33415
+ bounds.x,
33416
+ Math.max(area.y, area.y + area.height - height)
33417
+ );
33418
+ }
33419
+ };
33420
+ electron.ipcMain.on(MINI_VIEW_RESIZE, onResize);
33421
+ const onPainted = (event) => {
33422
+ if (window.isDestroyed() || event.sender !== window.webContents || event.senderFrame !== window.webContents.mainFrame)
33423
+ return;
33424
+ settlePaint(true);
33425
+ };
33426
+ electron.ipcMain.on(MINI_VIEW_PAINTED, onPainted);
33168
33427
  let disposed = false;
33169
33428
  function hide() {
33170
33429
  if (disposed || window.isDestroyed()) return;
@@ -33194,42 +33453,86 @@ function createMiniView(options) {
33194
33453
  }
33195
33454
  hide();
33196
33455
  });
33197
- function toggle() {
33198
- if (disposed) return;
33199
- if (!window.isDestroyed() && window.isVisible()) {
33200
- hide();
33456
+ function presentWhenReady() {
33457
+ if (disposed || window.isDestroyed()) return;
33458
+ const decision = miniViewPresentationFor({
33459
+ show: options.show,
33460
+ painted: paintedForLoad,
33461
+ timedOut: false
33462
+ });
33463
+ if (decision === "error-surface") {
33464
+ showErrorSurface();
33201
33465
  return;
33202
33466
  }
33467
+ if (decision === "present") {
33468
+ presentNow();
33469
+ return;
33470
+ }
33471
+ paintWaiters.push(() => {
33472
+ if (miniViewPresentationFor({
33473
+ show: options.show,
33474
+ painted: paintedForLoad,
33475
+ timedOut: false
33476
+ }) === "present")
33477
+ presentNow();
33478
+ });
33479
+ if (paintTimer === null) {
33480
+ paintTimer = setTimeout(() => {
33481
+ paintTimer = null;
33482
+ if (miniViewPresentationFor({
33483
+ show: options.show,
33484
+ painted: paintedForLoad,
33485
+ timedOut: true
33486
+ }) === "error-surface")
33487
+ showErrorSurface();
33488
+ }, MINI_VIEW_PAINT_TIMEOUT_MS);
33489
+ }
33490
+ }
33491
+ function presentNow() {
33492
+ if (disposed || window.isDestroyed()) return;
33203
33493
  positionForCursorDisplay();
33204
33494
  presentMiniView(window, options.show, {
33205
33495
  trigger: "mini-view",
33206
33496
  report: options.report
33207
33497
  });
33208
- if (options.show === "focus" && !window.isDestroyed()) {
33209
- const payload = { at: Date.now() };
33210
- window.webContents.send(MINI_VIEW_SUMMONED, payload);
33498
+ if (options.show === "focus") {
33499
+ window.webContents.send(MINI_VIEW_SUMMONED, {
33500
+ at: Date.now()
33501
+ });
33211
33502
  }
33212
33503
  }
33504
+ function toggle() {
33505
+ if (disposed) return;
33506
+ if (!window.isDestroyed() && window.isVisible()) {
33507
+ hide();
33508
+ return;
33509
+ }
33510
+ presentWhenReady();
33511
+ }
33213
33512
  function positionForCursorDisplay() {
33214
33513
  if (window.isDestroyed()) return;
33215
33514
  const display = electron.screen.getDisplayNearestPoint(
33216
33515
  electron.screen.getCursorScreenPoint()
33217
33516
  );
33218
33517
  const area = display.workArea;
33518
+ const height = window.isDestroyed() ? MINI_VIEW_HEIGHT : window.getContentBounds().height;
33219
33519
  const x = Math.round(area.x + (area.width - MINI_VIEW_WIDTH) / 2);
33220
- const desiredY = area.y + area.height / 3 - MINI_VIEW_HEIGHT / 2;
33520
+ const desiredY = area.y + area.height / 3 - height / 2;
33221
33521
  const y = Math.round(
33222
- Math.min(
33223
- Math.max(desiredY, area.y),
33224
- area.y + area.height - MINI_VIEW_HEIGHT
33225
- )
33522
+ Math.min(Math.max(desiredY, area.y), area.y + area.height - height)
33226
33523
  );
33227
33524
  window.setPosition(x, y);
33228
33525
  }
33229
33526
  function dispose() {
33230
33527
  if (disposed) return;
33231
33528
  disposed = true;
33529
+ electron.ipcMain.removeListener(MINI_VIEW_RESIZE, onResize);
33530
+ electron.ipcMain.removeListener(MINI_VIEW_PAINTED, onPainted);
33232
33531
  electron.ipcMain.removeHandler(MINI_VIEW_DISMISS);
33532
+ if (paintTimer !== null) {
33533
+ clearTimeout(paintTimer);
33534
+ paintTimer = null;
33535
+ }
33233
33536
  if (!window.isDestroyed()) window.destroy();
33234
33537
  }
33235
33538
  return { window, toggle, hide, dispose };
@@ -40376,9 +40679,15 @@ if (process.platform === "darwin" && electron.app.dock) {
40376
40679
  if (windowLaunch.hideDock) {
40377
40680
  electron.app.dock.hide();
40378
40681
  } else {
40682
+ electron.app.dock.show();
40379
40683
  electron.app.dock.setIcon(image);
40380
40684
  }
40381
40685
  }
40686
+ function reassertDockParticipation() {
40687
+ if (process.platform !== "darwin" || !electron.app.dock) return;
40688
+ if (windowLaunch.hideDock) return;
40689
+ electron.app.dock.show();
40690
+ }
40382
40691
  const telemetryLaunch = resolveTelemetryLaunch({
40383
40692
  env: launchEnv,
40384
40693
  projectKey: backendConfig.VITE_PUBLIC_POSTHOG_KEY
@@ -41566,24 +41875,37 @@ electron.app.whenReady().then(async () => {
41566
41875
  return false;
41567
41876
  }
41568
41877
  });
41569
- electron.ipcMain.handle("show-open-dialog", async (_, options) => {
41570
- if (!mainWindow) {
41878
+ electron.ipcMain.handle("show-open-dialog", async (event, options) => {
41879
+ const parent = electron.BrowserWindow.fromWebContents(event.sender) ?? mainWindow;
41880
+ if (!parent) {
41571
41881
  logger.error(
41572
- "Cannot show open dialog: mainWindow is not available.",
41882
+ "Cannot show open dialog: no window can own it.",
41573
41883
  LogFileType.BACKEND
41574
41884
  );
41575
41885
  return { canceled: true, filePaths: [] };
41576
41886
  }
41577
- const result = await electron.dialog.showOpenDialog(
41578
- mainWindow,
41579
- withRememberedDirectory(
41580
- "open-file",
41581
- options,
41582
- pickerFallbackDirectory()
41583
- )
41584
- );
41585
- rememberPickedDirectory("open-file", result.filePaths);
41586
- return result;
41887
+ const mini = miniView;
41888
+ const fromMini = mini !== null && !mini.window.isDestroyed() && event.sender === mini.window.webContents;
41889
+ const dialogPayload = { open: true };
41890
+ if (fromMini)
41891
+ mini.window.webContents.send(MINI_VIEW_DIALOG, dialogPayload);
41892
+ try {
41893
+ const result = await electron.dialog.showOpenDialog(
41894
+ parent,
41895
+ withRememberedDirectory(
41896
+ "open-file",
41897
+ options,
41898
+ pickerFallbackDirectory()
41899
+ )
41900
+ );
41901
+ rememberPickedDirectory("open-file", result.filePaths);
41902
+ return result;
41903
+ } finally {
41904
+ if (fromMini && mini !== null && !mini.window.isDestroyed())
41905
+ mini.window.webContents.send(MINI_VIEW_DIALOG, {
41906
+ open: false
41907
+ });
41908
+ }
41587
41909
  });
41588
41910
  electron.ipcMain.handle("select-directory", async () => {
41589
41911
  if (!mainWindow) {
@@ -41992,6 +42314,7 @@ electron.app.whenReady().then(async () => {
41992
42314
  };
41993
42315
  setupMainWindowWithUpdateService(launchSession, launchCatalogue);
41994
42316
  electron.app.on("activate", () => {
42317
+ reassertDockParticipation();
41995
42318
  if (electron.BrowserWindow.getAllWindows().length === 0) {
41996
42319
  if (quitState.isQuitting()) {
41997
42320
  reportSkippedWhileQuitting(
@@ -42031,6 +42354,13 @@ electron.app.on("window-all-closed", () => {
42031
42354
  electron.app.quit();
42032
42355
  return;
42033
42356
  }
42357
+ if (quitState.isQuitting()) {
42358
+ logger.info(
42359
+ "All windows closed as part of a quit in progress; will-quit owns the completion",
42360
+ LogFileType.BACKEND
42361
+ );
42362
+ return;
42363
+ }
42034
42364
  logger.info(
42035
42365
  "All windows closed, but keeping app active (macOS platform)",
42036
42366
  LogFileType.BACKEND
@@ -42070,7 +42400,7 @@ electron.app.on("will-quit", (event) => {
42070
42400
  failsafe.unref();
42071
42401
  void backendService.stop(false).then(() => {
42072
42402
  clearTimeout(failsafe);
42073
- electron.app.quit();
42403
+ electron.app.exit(0);
42074
42404
  }).catch((error) => {
42075
42405
  clearTimeout(failsafe);
42076
42406
  logger.error(
@@ -42089,22 +42419,33 @@ const holdQuitForSessionCookieSnapshot = createSessionCookieQuitHold({
42089
42419
  });
42090
42420
  electron.app.on("before-quit", async (event) => {
42091
42421
  quitState.begin();
42422
+ electron.globalShortcut.unregisterAll();
42423
+ miniViewRegistrar?.dispose();
42424
+ quickSendWatcher?.stop();
42425
+ miniView?.dispose();
42426
+ if (windowLaunch.mode === "headless") {
42427
+ launcherWatch?.stop();
42428
+ armHeadlessExitDeadline("the app is quitting");
42429
+ }
42092
42430
  if (await holdQuitForSessionCookieSnapshot(event)) return;
42093
42431
  logger.info("App is about to quit", LogFileType.BACKEND);
42432
+ if (!backendService.isOwnedCleanupComplete()) {
42433
+ try {
42434
+ await backendService.stop(false);
42435
+ } catch (error) {
42436
+ logger.error(
42437
+ "Owned backend stop rejected during quit; continuing the shutdown",
42438
+ LogFileType.BACKEND,
42439
+ error
42440
+ );
42441
+ }
42442
+ }
42094
42443
  if (parkedLaunches.length > 0) {
42095
42444
  reportParksAtQuit(
42096
42445
  parkedLaunches.map((parked) => parked.session),
42097
42446
  reportRaise
42098
42447
  );
42099
42448
  }
42100
- if (windowLaunch.mode === "headless") {
42101
- launcherWatch?.stop();
42102
- armHeadlessExitDeadline("the app is quitting");
42103
- }
42104
- electron.globalShortcut.unregisterAll();
42105
- miniViewRegistrar?.dispose();
42106
- quickSendWatcher?.stop();
42107
- miniView?.dispose();
42108
42449
  activeUpdateService?.quitForInFlightInstall("app quit");
42109
42450
  void stopBrowserHost();
42110
42451
  });
@@ -94,6 +94,9 @@ const MINI_VIEW_SUMMONED = "mini-view:summoned";
94
94
  const MINI_VIEW_DISMISS = "mini-view:dismiss";
95
95
  const MINI_VIEW_REGISTRATION = "mini-view:registration";
96
96
  const MINI_VIEW_REGISTRATION_GET = "mini-view:registration-get";
97
+ const MINI_VIEW_RESIZE = "mini-view:resize";
98
+ const MINI_VIEW_DIALOG = "mini-view:dialog";
99
+ const MINI_VIEW_PAINTED = "mini-view:painted";
97
100
  const MINI_VIEW_REGISTRATION_STATUSES = [
98
101
  "registered",
99
102
  "taken",
@@ -114,6 +117,9 @@ function isMiniViewRegistrationState(value) {
114
117
  state.status
115
118
  );
116
119
  }
120
+ function isMiniViewDialogPayload(value) {
121
+ return value !== null && typeof value === "object" && typeof value.open === "boolean";
122
+ }
117
123
  const OPEN_SESSION_FLAG = "--open-session";
118
124
  const OPEN_CATALOGUE_FLAG = "--open-catalogue";
119
125
  const SESSION_ID = /^[a-f0-9]{12}$/;
@@ -1019,6 +1025,37 @@ const api = {
1019
1025
  },
1020
1026
  /** This window -> main: put the mini window away, naming the act. */
1021
1027
  dismiss: (reason) => electron.ipcRenderer.invoke(MINI_VIEW_DISMISS, reason),
1028
+ /**
1029
+ * This window -> main: the content height this frame needs (design R2's
1030
+ * measure -> IPC -> resize). Fire-and-forget by design: the frame measures
1031
+ * on every layout settle, there is no answer to await, and main owns the
1032
+ * clamp - see `MINI_VIEW_RESIZE` in `src/shared/mini-view.ts`.
1033
+ */
1034
+ resize: (height) => {
1035
+ electron.ipcRenderer.send(MINI_VIEW_RESIZE, { height });
1036
+ },
1037
+ /**
1038
+ * This window -> main: the document has committed its first paint, so the
1039
+ * window may be shown without flashing white (see `MINI_VIEW_PAINTED`).
1040
+ * Fire-and-forget, and sent once per load.
1041
+ */
1042
+ painted: () => {
1043
+ electron.ipcRenderer.send(MINI_VIEW_PAINTED);
1044
+ },
1045
+ /**
1046
+ * Main -> this window: a native dialog the window opened is up, or answered.
1047
+ * The frame latches its blur-hide guard for the duration (see
1048
+ * `MINI_VIEW_DIALOG`).
1049
+ */
1050
+ onDialog: (callback) => {
1051
+ const handler = (_event, payload) => {
1052
+ if (isMiniViewDialogPayload(payload)) callback(payload);
1053
+ };
1054
+ electron.ipcRenderer.on(MINI_VIEW_DIALOG, handler);
1055
+ return () => {
1056
+ electron.ipcRenderer.removeListener(MINI_VIEW_DIALOG, handler);
1057
+ };
1058
+ },
1022
1059
  /** Main -> every renderer: the live global-shortcut state. */
1023
1060
  onRegistration: (callback) => {
1024
1061
  const handler = (_event, state) => {