local-operator-ui 0.41.1 → 0.42.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 (99) hide show
  1. package/out/main/index.js +1242 -226
  2. package/out/preload/index.js +102 -4
  3. package/out/renderer/assets/{_basePickBy-BP7Q8i4W.js → _basePickBy-KfApUmWE.js} +1 -1
  4. package/out/renderer/assets/{_baseUniq-D36dAcoY.js → _baseUniq-DXd1h_x3.js} +1 -1
  5. package/out/renderer/assets/{agent-details-page-D3ljlPdf.js → agent-details-page-D-oKoyjm.js} +2 -2
  6. package/out/renderer/assets/{agent-hub-page-BMyIHnNM.js → agent-hub-page-CMbCN_Bg.js} +2 -2
  7. package/out/renderer/assets/agents-page-COpZu9CS.js +7 -0
  8. package/out/renderer/assets/{arc-GVvow4so.js → arc-Dim07OWx.js} +1 -1
  9. package/out/renderer/assets/{architectureDiagram-IEHRJDOE-BjGwXAtH.js → architectureDiagram-IEHRJDOE-BdwEpGi5.js} +1 -1
  10. package/out/renderer/assets/{blockDiagram-JOT3LUYC-BTuigKlt.js → blockDiagram-JOT3LUYC-BcLFfkMT.js} +1 -1
  11. package/out/renderer/assets/{book-open-C1uak1LZ.js → book-open-C2DZijqP.js} +1 -1
  12. package/out/renderer/assets/browser-page-DzwNid1n.js +1 -0
  13. package/out/renderer/assets/{browser-webauthn-prompt-CzYoJfO4.js → browser-webauthn-prompt-C4aaSeT0.js} +1 -1
  14. package/out/renderer/assets/{building-2-Bpz1qaDa.js → building-2-DfFTqKaJ.js} +1 -1
  15. package/out/renderer/assets/{c4Diagram-VJAJSXHY-DK7eaKE8.js → c4Diagram-VJAJSXHY-Dm38eTOl.js} +1 -1
  16. package/out/renderer/assets/channel-CefUnWw7.js +1 -0
  17. package/out/renderer/assets/{chunk-4BMEZGHF-DkQrsDhM.js → chunk-4BMEZGHF-94enK1_-.js} +1 -1
  18. package/out/renderer/assets/{chunk-A2AXSNBT-guh0INPR.js → chunk-A2AXSNBT-YiigwCRa.js} +1 -1
  19. package/out/renderer/assets/{chunk-AEK57VVT-DvgIx6-r.js → chunk-AEK57VVT-Bo9PQDfg.js} +1 -1
  20. package/out/renderer/assets/{chunk-D6G4REZN-FNdvnFYo.js → chunk-D6G4REZN-vwOZoeTn.js} +1 -1
  21. package/out/renderer/assets/{chunk-RZ5BOZE2-CgCQC9Mn.js → chunk-RZ5BOZE2-BY9NQkOq.js} +1 -1
  22. package/out/renderer/assets/{chunk-XZIHB7SX-D1VRTTc4.js → chunk-XZIHB7SX-D31wKKrQ.js} +1 -1
  23. package/out/renderer/assets/classDiagram-GIVACNV2-BKByQj4B.js +1 -0
  24. package/out/renderer/assets/classDiagram-v2-COTLJTTW-BKByQj4B.js +1 -0
  25. package/out/renderer/assets/clone-DpeVXG90.js +1 -0
  26. package/out/renderer/assets/{console-mirror-DRkz3vUY.js → console-mirror-ClsQ7RUr.js} +1 -1
  27. package/out/renderer/assets/{consoleCapture-BJfSUatx.js → consoleCapture-DHR2J1Z2.js} +1 -1
  28. package/out/renderer/assets/{conversation-input-sync-CJU5GIzR.js → conversation-input-sync-CxT2etWK.js} +271 -251
  29. package/out/renderer/assets/{dagre-OKDRZEBW-DeeYQh6O.js → dagre-OKDRZEBW-DV8UoEV8.js} +1 -1
  30. package/out/renderer/assets/{diagram-SSKATNLV-DD-xNxKH.js → diagram-SSKATNLV-KF8nQEWd.js} +1 -1
  31. package/out/renderer/assets/{diagram-VNBRO52H-YAoPlXaR.js → diagram-VNBRO52H-P7VEv-Kl.js} +1 -1
  32. package/out/renderer/assets/{erDiagram-Q7BY3M3F-OKzGXyMI.js → erDiagram-Q7BY3M3F-B17XYgVW.js} +1 -1
  33. package/out/renderer/assets/{error-boundary-ZYkSCpZC.js → error-boundary-DBgLhXDJ.js} +1 -1
  34. package/out/renderer/assets/{flowDiagram-4HSFHLVR-BG-3pPqS.js → flowDiagram-4HSFHLVR-BuuUG-g9.js} +1 -1
  35. package/out/renderer/assets/{ganttDiagram-APWFNJXF-CRyD5vsE.js → ganttDiagram-APWFNJXF-D8R9R-AO.js} +1 -1
  36. package/out/renderer/assets/{gitGraphDiagram-7IBYFJ6S-aly2C__q.js → gitGraphDiagram-7IBYFJ6S-fr4ArrNz.js} +1 -1
  37. package/out/renderer/assets/{graph-CCWwMmhg.js → graph-nV36Izm2.js} +1 -1
  38. package/out/renderer/assets/{hash-BN0ECl3H.js → hash-0qLX32ng.js} +1 -1
  39. package/out/renderer/assets/{heart-tKYqMVIH.js → heart-DFAcW-lm.js} +1 -1
  40. package/out/renderer/assets/{index-DaCf2W9T.js → index-BKg4FYjW.js} +1 -1
  41. package/out/renderer/assets/index-BlshO0D3.css +1 -0
  42. package/out/renderer/assets/{index-BaNzqqGN.js → index-BmixXvR4.js} +1 -1
  43. package/out/renderer/assets/index-D6Di-Q2H.js +57 -0
  44. package/out/renderer/assets/index-DQKtrmMo.js +830 -0
  45. package/out/renderer/assets/{infoDiagram-PH2N3AL5-BY536sF5.js → infoDiagram-PH2N3AL5-DmrijSS0.js} +1 -1
  46. package/out/renderer/assets/{installer-Dw1P6j8q.js → installer-4TSvERLS.js} +1 -1
  47. package/out/renderer/assets/{journeyDiagram-U35MCT3I-DGvx__t6.js → journeyDiagram-U35MCT3I-BwwXu0up.js} +1 -1
  48. package/out/renderer/assets/{kanban-definition-NDS4AKOZ-CG2Lgkpw.js → kanban-definition-NDS4AKOZ-CDbDK3QO.js} +1 -1
  49. package/out/renderer/assets/{layout-vpRrwE7T.js → layout-B7RIhnQw.js} +1 -1
  50. package/out/renderer/assets/{legacy-agents-page-D6Yma1Gm.js → legacy-agents-page-DFGXD5LW.js} +6 -6
  51. package/out/renderer/assets/library-page-DPid0cmw.js +157 -0
  52. package/out/renderer/assets/{mermaid.core-COeGB8I0.js → mermaid.core-DcMJPeY9.js} +5 -5
  53. package/out/renderer/assets/{mesh-page-PabU05tN.js → mesh-page-CIV9irJF.js} +1 -1
  54. package/out/renderer/assets/{mindmap-definition-ALO5MXBD-B0hkjZLY.js → mindmap-definition-ALO5MXBD-aLvI45Tt.js} +1 -1
  55. package/out/renderer/assets/mini-leTyoKhc.js +1 -0
  56. package/out/renderer/assets/{notebook-pen-C3CwzwGA.js → notebook-pen-ClukfLau.js} +1 -1
  57. package/out/renderer/assets/{org-access-BacMtLkn.js → org-access-BmjQJdOO.js} +1 -1
  58. package/out/renderer/assets/{org-surface-gate-tkB025lu.js → org-surface-gate-B66nUFl-.js} +1 -1
  59. package/out/renderer/assets/{page-header-6mrV9W-E.js → page-header-C8bsR5wI.js} +1 -1
  60. package/out/renderer/assets/path-utils-DbnynqGJ.js +58 -0
  61. package/out/renderer/assets/{pieDiagram-IB7DONF6-DfFtQXU5.js → pieDiagram-IB7DONF6-DvzUPikw.js} +1 -1
  62. package/out/renderer/assets/projects-page-4Cv9HMNh.js +45 -0
  63. package/out/renderer/assets/{publication-validation-B0Mf3mmP.js → publication-validation-BlKeNjlY.js} +1 -1
  64. package/out/renderer/assets/{quadrantDiagram-7GDLP6J5-C4eDJWaK.js → quadrantDiagram-7GDLP6J5-DFsFogdr.js} +1 -1
  65. package/out/renderer/assets/{radar-MK3ICKWK-BEv1HS9Q.js → radar-MK3ICKWK-YGPF88zl.js} +1 -1
  66. package/out/renderer/assets/{radient-auth-buttons-BX5vf982.js → radient-auth-buttons-syGP58Ed.js} +1 -1
  67. package/out/renderer/assets/{requirementDiagram-KVF5MWMF-B7f3qUcB.js → requirementDiagram-KVF5MWMF-x2D5VXdu.js} +1 -1
  68. package/out/renderer/assets/{sankeyDiagram-QLVOVGJD-xM7E-74l.js → sankeyDiagram-QLVOVGJD-DA4pKMXD.js} +1 -1
  69. package/out/renderer/assets/{schedules-page-5q7pvL1H.js → schedules-page-CLrwsPcv.js} +1 -1
  70. package/out/renderer/assets/{sequenceDiagram-X6HHIX6F-upwR02kU.js → sequenceDiagram-X6HHIX6F-BUX2Yk2J.js} +1 -1
  71. package/out/renderer/assets/{settings-page-DcnGr-GP.js → settings-page-BAVKJQXi.js} +9 -9
  72. package/out/renderer/assets/{smartart-CYnMNs5u.js → smartart-ipuU9VIr.js} +1 -1
  73. package/out/renderer/assets/{stateDiagram-DGXRK772-BqTri7Q-.js → stateDiagram-DGXRK772-BsVvaiaW.js} +1 -1
  74. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-BvVuptXT.js +1 -0
  75. package/out/renderer/assets/team-bridge-page-DU_d4Yfc.js +16 -0
  76. package/out/renderer/assets/{timeline-definition-BDJGKUSR-Bi2is17s.js → timeline-definition-BDJGKUSR-Doja7siy.js} +1 -1
  77. package/out/renderer/assets/{use-download-agent-mutation-PHtokPlg.js → use-download-agent-mutation-CH4V_o78.js} +2 -2
  78. package/out/renderer/assets/{use-session-snapshot-B5sxthdQ.js → use-session-snapshot-7A4rQnOH.js} +1 -1
  79. package/out/renderer/assets/{xychartDiagram-VJFVF3MP-B0TrjzLJ.js → xychartDiagram-VJFVF3MP-D2e6wZz8.js} +1 -1
  80. package/out/renderer/console-capture.html +5 -5
  81. package/out/renderer/index.html +7 -7
  82. package/out/renderer/installer.html +4 -4
  83. package/out/renderer/mini.html +7 -7
  84. package/package.json +4 -3
  85. package/out/renderer/assets/agents-page-mMqgQd2V.js +0 -7
  86. package/out/renderer/assets/browser-page-B3mjDx8k.js +0 -1
  87. package/out/renderer/assets/channel-B05rL6In.js +0 -1
  88. package/out/renderer/assets/classDiagram-GIVACNV2-DpB8-8cT.js +0 -1
  89. package/out/renderer/assets/classDiagram-v2-COTLJTTW-DpB8-8cT.js +0 -1
  90. package/out/renderer/assets/clone-BHHjv3HP.js +0 -1
  91. package/out/renderer/assets/index-BDzV3bGH.js +0 -57
  92. package/out/renderer/assets/index-CRjnV9wx.js +0 -835
  93. package/out/renderer/assets/index-ZovyVTxQ.css +0 -1
  94. package/out/renderer/assets/library-page-B0gC-rRh.js +0 -157
  95. package/out/renderer/assets/mini-Df5C4rpd.js +0 -1
  96. package/out/renderer/assets/path-utils-Ct0VTUZS.js +0 -58
  97. package/out/renderer/assets/projects-page-CoHTKLZH.js +0 -45
  98. package/out/renderer/assets/stateDiagram-v2-YXO3MK2T-D8Sl1QhX.js +0 -1
  99. package/out/renderer/assets/team-bridge-page-vnNJdCi3.js +0 -16
package/out/main/index.js CHANGED
@@ -200,6 +200,8 @@ zod.z.object({
200
200
  }).passthrough()
201
201
  }).passthrough()
202
202
  }).passthrough();
203
+ const MAX_ATTACHMENT_UPLOAD_BYTES = 32 * 1024 * 1024;
204
+ const DESKTOP_MESSAGE_MAX_FILES = 8;
203
205
  const SIDEBAR_DESTINATION_IDS = [
204
206
  "aida",
205
207
  "library",
@@ -213,9 +215,9 @@ const SIDEBAR_DESTINATION_IDS = [
213
215
  ];
214
216
  const JOURNAL_ORDER_QUERY_BYTES = 6 * 1024;
215
217
  const JOURNAL_ORDER_ID_LIMIT = 512;
216
- function appendJournalOrderIds(query, path2, ids) {
218
+ function appendJournalOrderIds(query2, path2, ids) {
217
219
  let bytes = new TextEncoder().encode(
218
- `${path2}${query.size ? `?${query}` : ""}`
220
+ `${path2}${query2.size ? `?${query2}` : ""}`
219
221
  ).length;
220
222
  const selected = [];
221
223
  for (const id2 of ids) {
@@ -223,7 +225,7 @@ function appendJournalOrderIds(query, path2, ids) {
223
225
  const encoded = new URLSearchParams({ order_ids: id2 }).toString();
224
226
  const extra = encoded.length + 1;
225
227
  if (bytes + extra > JOURNAL_ORDER_QUERY_BYTES) break;
226
- query.append("order_ids", id2);
228
+ query2.append("order_ids", id2);
227
229
  selected.push(id2);
228
230
  bytes += extra;
229
231
  }
@@ -251,7 +253,21 @@ const libraryAssetId = zod.z.string().regex(/^la_[0-9a-f]{16}$/);
251
253
  const libraryTagId = zod.z.string().min(1).max(128).regex(/^[A-Za-z0-9_.:-]+$/);
252
254
  const sessionImage = zod.z.object({
253
255
  data_b64: zod.z.string().min(1).max(1e6),
254
- mime_type: zod.z.enum(["image/png", "image/jpeg", "image/gif", "image/webp"])
256
+ mime_type: zod.z.enum(["image/png", "image/jpeg", "image/gif", "image/webp"]),
257
+ /**
258
+ * Where the image came from, when it came from disk. Provenance only: the
259
+ * daemon carries it to `ImageContent.path` and every provider serializer
260
+ * ignores it (attachment contract v1 §1.3). Absent on pasted images, which
261
+ * have no path.
262
+ */
263
+ origin_path: zod.z.string().max(4096).optional()
264
+ }).strict();
265
+ const sessionFile = zod.z.object({
266
+ digest: zod.z.string().regex(/^[0-9a-f]{32}$/),
267
+ name: zod.z.string().max(1024).optional(),
268
+ mime_type: zod.z.string().max(255).optional(),
269
+ bytes: zod.z.number().int().min(0).optional(),
270
+ path: zod.z.string().max(4096).optional()
255
271
  }).strict();
256
272
  const DESKTOP_MESSAGE_MAX_CHARS = 2e5;
257
273
  const DESKTOP_MESSAGE_MAX_IMAGES = 8;
@@ -1222,6 +1238,13 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
1222
1238
  requestId,
1223
1239
  text: zod.z.string().max(DESKTOP_MESSAGE_MAX_CHARS),
1224
1240
  images: zod.z.array(sessionImage).max(DESKTOP_MESSAGE_MAX_IMAGES).optional(),
1241
+ /**
1242
+ * Uploaded files this message references, bounded to the backend's
1243
+ * `Prompt.files` ceiling. Absent rather than empty when the send carries
1244
+ * none, so an older harness's `extra=forbid` sees a legacy body - the same
1245
+ * rule the optional fields below follow.
1246
+ */
1247
+ files: zod.z.array(sessionFile).max(DESKTOP_MESSAGE_MAX_FILES).optional(),
1225
1248
  mode: zod.z.enum(["prompt", "steer"]).optional(),
1226
1249
  /*
1227
1250
  * HOW THE MESSAGE WAS PRODUCED (arch §4.2), and the harness gate is what
@@ -1318,7 +1341,28 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
1318
1341
  * its own sentence once the response row exists; this client does not
1319
1342
  * pre-judge that, it renders the owner's refusal.
1320
1343
  */
1321
- revise: zod.z.boolean().optional()
1344
+ revise: zod.z.boolean().optional(),
1345
+ /**
1346
+ * PUT AN OPEN ASK AWAY instead of answering it (harness PR #2833, SA-D5).
1347
+ *
1348
+ * A modifier on the SAME `{ask_id}` body rather than a fourth op: a dismissal
1349
+ * is the same epoch-free queued-ask door an answer is, addressed by an id that
1350
+ * outlives any owner. Only `true` selects the shape — `false` is the same
1351
+ * statement as omitting it — and the shape rules below refuse it beside
1352
+ * answers/decline/revise, because no single body may answer and dismiss one ask.
1353
+ *
1354
+ * GATED ON `features.ask_dismiss` at the surface that offers the control: the
1355
+ * backend's `Answer` is `extra="forbid"`, so an older daemon answers 422 to this
1356
+ * field rather than ignoring it.
1357
+ */
1358
+ dismiss: zod.z.boolean().optional(),
1359
+ /**
1360
+ * Why the operator put it away: `answered_in_chat` (the conversation already
1361
+ * answered it) or `moot` (no longer needed). Optional; the ask log records
1362
+ * `moot` when absent. Refused without `dismiss: true` — the same no-op rule
1363
+ * `revise` follows above.
1364
+ */
1365
+ reason: zod.z.enum(["answered_in_chat", "moot"]).optional()
1322
1366
  }).strict(),
1323
1367
  zod.z.object({
1324
1368
  op: zod.z.literal("sessions.seen"),
@@ -2423,6 +2467,7 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
2423
2467
  }).strict(),
2424
2468
  zod.z.object({
2425
2469
  op: zod.z.literal("team_bridge.conversations.create"),
2470
+ expected_account_id: id$1.optional(),
2426
2471
  tenant_id: id$1.optional(),
2427
2472
  kind: zod.z.enum(["dm", "group", "channel"]),
2428
2473
  name: zod.z.string().min(1).max(128).optional(),
@@ -2440,6 +2485,8 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
2440
2485
  }).strict(),
2441
2486
  zod.z.object({
2442
2487
  op: zod.z.literal("team_bridge.conversations.lifecycle"),
2488
+ expected_account_id: id$1.optional(),
2489
+ tenant_id: id$1.optional(),
2443
2490
  conversation_id: id$1,
2444
2491
  action: zod.z.enum(["archive", "unarchive", "join", "leave"])
2445
2492
  }).strict(),
@@ -2456,17 +2503,20 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
2456
2503
  */
2457
2504
  zod.z.object({
2458
2505
  op: zod.z.literal("team_bridge.conversations.join"),
2506
+ expected_account_id: id$1.optional(),
2459
2507
  conversation_id: id$1,
2460
2508
  tenant_id: id$1.optional()
2461
2509
  }).strict(),
2462
2510
  zod.z.object({
2463
2511
  op: zod.z.literal("team_bridge.conversations.notify"),
2512
+ expected_account_id: id$1.optional(),
2464
2513
  conversation_id: id$1,
2465
2514
  tenant_id: id$1.optional(),
2466
2515
  notify: zod.z.enum(["all", "mention", "mute"])
2467
2516
  }).strict(),
2468
2517
  zod.z.object({
2469
2518
  op: zod.z.literal("team_bridge.conversations.members.add"),
2519
+ expected_account_id: id$1.optional(),
2470
2520
  conversation_id: id$1,
2471
2521
  tenant_id: id$1.optional(),
2472
2522
  members: zod.z.array(
@@ -2480,11 +2530,14 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
2480
2530
  }).strict(),
2481
2531
  zod.z.object({
2482
2532
  op: zod.z.literal("team_bridge.conversations.members.remove"),
2533
+ expected_account_id: id$1.optional(),
2534
+ tenant_id: id$1.optional(),
2483
2535
  conversation_id: id$1,
2484
2536
  account_id: id$1
2485
2537
  }).strict(),
2486
2538
  zod.z.object({
2487
2539
  op: zod.z.literal("team_bridge.conversations.consent"),
2540
+ expected_account_id: id$1.optional(),
2488
2541
  conversation_id: id$1,
2489
2542
  account_id: id$1,
2490
2543
  decision: zod.z.enum(["accept", "decline"]),
@@ -2509,6 +2562,7 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
2509
2562
  }).strict(),
2510
2563
  zod.z.object({
2511
2564
  op: zod.z.literal("team_bridge.messages.send"),
2565
+ expected_account_id: id$1.optional(),
2512
2566
  conversation_id: id$1,
2513
2567
  tenant_id: id$1.optional(),
2514
2568
  client_ref: id$1,
@@ -2531,6 +2585,7 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
2531
2585
  }).strict(),
2532
2586
  zod.z.object({
2533
2587
  op: zod.z.literal("team_bridge.read"),
2588
+ expected_account_id: id$1.optional(),
2534
2589
  conversation_id: id$1,
2535
2590
  tenant_id: id$1.optional(),
2536
2591
  seq: zod.z.number().int().nonnegative().optional(),
@@ -3058,6 +3113,14 @@ const desktopRequestUnion = zod.z.discriminatedUnion("op", [
3058
3113
  ]);
3059
3114
  const desktopRequestSchema = desktopRequestUnion.superRefine(
3060
3115
  (request, ctx) => {
3116
+ if (request.op.startsWith("team_bridge.") && "expected_account_id" in request && request.expected_account_id && (!("tenant_id" in request) || !request.tenant_id)) {
3117
+ ctx.addIssue({
3118
+ code: zod.z.ZodIssueCode.custom,
3119
+ path: ["tenant_id"],
3120
+ message: "Guarded conversation writes require an explicit tenant"
3121
+ });
3122
+ return;
3123
+ }
3061
3124
  if (request.op === "hub.skillPull" || request.op === "hub.skillSubscribe" || request.op === "hub.skillPublish") {
3062
3125
  if (request.visibility === "public" && request.tenantId !== null || request.visibility === "org" && !request.tenantId) {
3063
3126
  ctx.addIssue({
@@ -3147,7 +3210,22 @@ const desktopRequestSchema = desktopRequestUnion.superRefine(
3147
3210
  message: "ask_id must be a non-empty string",
3148
3211
  path: ["askId"]
3149
3212
  });
3150
- if (request.decline !== true && !request.answers)
3213
+ if (request.dismiss === true) {
3214
+ if (request.answers !== void 0 || request.decline === true || request.revise === true)
3215
+ ctx.addIssue({
3216
+ code: zod.z.ZodIssueCode.custom,
3217
+ message: "A dismissal carries no answers, decline or revise",
3218
+ path: ["dismiss"]
3219
+ });
3220
+ return;
3221
+ }
3222
+ if (request.reason !== void 0)
3223
+ ctx.addIssue({
3224
+ code: zod.z.ZodIssueCode.custom,
3225
+ message: "A reason applies to a dismissal",
3226
+ path: ["reason"]
3227
+ });
3228
+ else if (request.decline !== true && !request.answers)
3151
3229
  ctx.addIssue({
3152
3230
  code: zod.z.ZodIssueCode.custom,
3153
3231
  message: "A queued-ask answer needs answers or decline",
@@ -3191,6 +3269,18 @@ const desktopRequestSchema = desktopRequestUnion.superRefine(
3191
3269
  message: "revise applies to a queued ask",
3192
3270
  path: ["revise"]
3193
3271
  });
3272
+ if (request.dismiss === true)
3273
+ ctx.addIssue({
3274
+ code: zod.z.ZodIssueCode.custom,
3275
+ message: "dismiss applies to a queued ask",
3276
+ path: ["dismiss"]
3277
+ });
3278
+ if (request.reason !== void 0)
3279
+ ctx.addIssue({
3280
+ code: zod.z.ZodIssueCode.custom,
3281
+ message: "reason applies to a queued ask",
3282
+ path: ["reason"]
3283
+ });
3194
3284
  return;
3195
3285
  }
3196
3286
  if (request.op === "sessions.create" && request.purpose === void 0) {
@@ -3631,6 +3721,13 @@ function harnessImportBody(request) {
3631
3721
  ...request.maxSessions === void 0 ? {} : { max_sessions: request.maxSessions }
3632
3722
  };
3633
3723
  }
3724
+ function conversationOwnerQuery(request) {
3725
+ const query2 = new URLSearchParams();
3726
+ if (request.tenant_id) query2.set("tenant_id", request.tenant_id);
3727
+ if (request.expected_account_id)
3728
+ query2.set("expected_account_id", request.expected_account_id);
3729
+ return query2.size ? `?${query2.toString()}` : "";
3730
+ }
3634
3731
  function tenantQuery(tenantId) {
3635
3732
  return tenantId === void 0 ? "" : `?tenant_id=${encodeURIComponent(tenantId)}`;
3636
3733
  }
@@ -3732,10 +3829,10 @@ function desktopEndpoint(request) {
3732
3829
  case "imports.sources":
3733
3830
  return { path: "/v1/desktop/imports/sources", method: "GET" };
3734
3831
  case "imports.sessions": {
3735
- const query = new URLSearchParams({ source: request.source });
3736
- if (request.includeSubagents) query.set("include_subagents", "true");
3832
+ const query2 = new URLSearchParams({ source: request.source });
3833
+ if (request.includeSubagents) query2.set("include_subagents", "true");
3737
3834
  return {
3738
- path: `/v1/desktop/imports/sessions?${query.toString()}`,
3835
+ path: `/v1/desktop/imports/sessions?${query2.toString()}`,
3739
3836
  method: "GET"
3740
3837
  };
3741
3838
  }
@@ -4000,13 +4097,13 @@ function desktopEndpoint(request) {
4000
4097
  method: "POST"
4001
4098
  };
4002
4099
  case "sessions.search": {
4003
- const query = new URLSearchParams({
4100
+ const query2 = new URLSearchParams({
4004
4101
  q: request.q,
4005
4102
  limit: String(request.limit ?? SESSION_SEARCH_DEFAULT_LIMIT)
4006
4103
  });
4007
- if (request.include_archived) query.set("include_archived", "true");
4104
+ if (request.include_archived) query2.set("include_archived", "true");
4008
4105
  return {
4009
- path: `/v1/desktop/sessions/search?${query}`,
4106
+ path: `/v1/desktop/sessions/search?${query2}`,
4010
4107
  method: "GET"
4011
4108
  };
4012
4109
  }
@@ -4086,23 +4183,23 @@ function desktopEndpoint(request) {
4086
4183
  }
4087
4184
  };
4088
4185
  case "sessions.get": {
4089
- const query = new URLSearchParams();
4186
+ const query2 = new URLSearchParams();
4090
4187
  if (request.journalOrder) {
4091
- query.set("journal_order", "1");
4092
- if (request.peerJournalSource) query.set("peer_journal_source", "1");
4188
+ query2.set("journal_order", "1");
4189
+ if (request.peerJournalSource) query2.set("peer_journal_source", "1");
4093
4190
  appendJournalOrderIds(
4094
- query,
4191
+ query2,
4095
4192
  `/v1/desktop/sessions/${request.sessionId}`,
4096
4193
  request.orderIds ?? []
4097
4194
  );
4098
4195
  }
4099
4196
  return {
4100
- path: `/v1/desktop/sessions/${request.sessionId}${query.size ? `?${query}` : ""}`,
4197
+ path: `/v1/desktop/sessions/${request.sessionId}${query2.size ? `?${query2}` : ""}`,
4101
4198
  method: "GET"
4102
4199
  };
4103
4200
  }
4104
4201
  case "sessions.files": {
4105
- const query = new URLSearchParams({
4202
+ const query2 = new URLSearchParams({
4106
4203
  limit: String(request.limit ?? 200)
4107
4204
  });
4108
4205
  for (const key of [
@@ -4114,10 +4211,10 @@ function desktopEndpoint(request) {
4114
4211
  "cursor"
4115
4212
  ]) {
4116
4213
  const value = request[key];
4117
- if (value !== void 0) query.set(key, value);
4214
+ if (value !== void 0) query2.set(key, value);
4118
4215
  }
4119
4216
  return {
4120
- path: `/v1/desktop/sessions/${encodeURIComponent(request.sessionId)}/files?${query}`,
4217
+ path: `/v1/desktop/sessions/${encodeURIComponent(request.sessionId)}/files?${query2}`,
4121
4218
  method: "GET"
4122
4219
  };
4123
4220
  }
@@ -4127,34 +4224,34 @@ function desktopEndpoint(request) {
4127
4224
  method: "POST"
4128
4225
  };
4129
4226
  case "sessions.history": {
4130
- const query = new URLSearchParams({
4227
+ const query2 = new URLSearchParams({
4131
4228
  limit: String(request.limit ?? 100)
4132
4229
  });
4133
- if (request.beforeId) query.set("before_id", request.beforeId);
4230
+ if (request.beforeId) query2.set("before_id", request.beforeId);
4134
4231
  if (request.openFrame) {
4135
- query.set("open_frame", "1");
4136
- query.set("supplements", "1");
4232
+ query2.set("open_frame", "1");
4233
+ query2.set("supplements", "1");
4137
4234
  }
4138
4235
  if (request.journalOrder) {
4139
- query.set("journal_order", "1");
4140
- if (request.peerJournalSource) query.set("peer_journal_source", "1");
4236
+ query2.set("journal_order", "1");
4237
+ if (request.peerJournalSource) query2.set("peer_journal_source", "1");
4141
4238
  if (request.sourceRead === "metadata")
4142
- query.set("source_read", "metadata");
4239
+ query2.set("source_read", "metadata");
4143
4240
  else if (request.sourceRead === "payload" && request.expectedJournalGeneration)
4144
- query.set("source_read", "payload");
4241
+ query2.set("source_read", "payload");
4145
4242
  if (request.expectedJournalGeneration)
4146
- query.set(
4243
+ query2.set(
4147
4244
  "expected_journal_generation",
4148
4245
  request.expectedJournalGeneration
4149
4246
  );
4150
4247
  appendJournalOrderIds(
4151
- query,
4248
+ query2,
4152
4249
  `/v1/desktop/sessions/${request.sessionId}/history`,
4153
4250
  request.orderIds ?? []
4154
4251
  );
4155
4252
  }
4156
4253
  return {
4157
- path: `/v1/desktop/sessions/${request.sessionId}/history?${query}`,
4254
+ path: `/v1/desktop/sessions/${request.sessionId}/history?${query2}`,
4158
4255
  method: "GET"
4159
4256
  };
4160
4257
  }
@@ -4179,22 +4276,22 @@ function desktopEndpoint(request) {
4179
4276
  }
4180
4277
  };
4181
4278
  case "sessions.find": {
4182
- const query = new URLSearchParams({
4279
+ const query2 = new URLSearchParams({
4183
4280
  q: request.q,
4184
4281
  limit: String(request.limit ?? THREAD_FIND_DEFAULT_LIMIT)
4185
4282
  });
4186
4283
  return {
4187
- path: `/v1/desktop/sessions/${request.sessionId}/find?${query}`,
4284
+ path: `/v1/desktop/sessions/${request.sessionId}/find?${query2}`,
4188
4285
  method: "GET"
4189
4286
  };
4190
4287
  }
4191
4288
  case "subagents.transcript": {
4192
- const query = new URLSearchParams({
4289
+ const query2 = new URLSearchParams({
4193
4290
  limit: String(request.limit ?? 100)
4194
4291
  });
4195
- if (request.beforeId) query.set("before_id", request.beforeId);
4292
+ if (request.beforeId) query2.set("before_id", request.beforeId);
4196
4293
  return {
4197
- path: `/v1/desktop/sessions/${request.sessionId}/children/${request.childId}/transcript?${query}`,
4294
+ path: `/v1/desktop/sessions/${request.sessionId}/children/${request.childId}/transcript?${query2}`,
4198
4295
  method: "GET"
4199
4296
  };
4200
4297
  }
@@ -4206,6 +4303,7 @@ function desktopEndpoint(request) {
4206
4303
  request_id: request.requestId,
4207
4304
  text: request.text,
4208
4305
  images: request.images ?? [],
4306
+ ...request.files?.length ? { files: request.files } : {},
4209
4307
  mode: request.mode ?? "prompt",
4210
4308
  /*
4211
4309
  * ABSENT, not empty, when the app has nothing to say: an older harness
@@ -4246,7 +4344,16 @@ function desktopEndpoint(request) {
4246
4344
  * body that carried `revise: false` on every ordinary submit would be
4247
4345
  * asserting an intent it does not have.
4248
4346
  */
4249
- ...request.revise === true ? { revise: true } : {}
4347
+ ...request.revise === true ? { revise: true } : {},
4348
+ /*
4349
+ * The dismissal modifier and its reason, on the same absent-when-not-
4350
+ * asked-for rule: an older harness refuses the field outright
4351
+ * (`extra="forbid"`), so an ordinary answer must not carry it.
4352
+ */
4353
+ ...request.dismiss === true ? {
4354
+ dismiss: true,
4355
+ ...request.reason !== void 0 ? { reason: request.reason } : {}
4356
+ } : {}
4250
4357
  }
4251
4358
  };
4252
4359
  return {
@@ -4424,12 +4531,12 @@ function desktopEndpoint(request) {
4424
4531
  method: "DELETE"
4425
4532
  };
4426
4533
  case "legacy.models": {
4427
- const query = new URLSearchParams();
4428
- if (request.provider) query.set("provider", request.provider);
4429
- if (request.sort) query.set("sort", request.sort);
4430
- if (request.direction) query.set("direction", request.direction);
4534
+ const query2 = new URLSearchParams();
4535
+ if (request.provider) query2.set("provider", request.provider);
4536
+ if (request.sort) query2.set("sort", request.sort);
4537
+ if (request.direction) query2.set("direction", request.direction);
4431
4538
  return {
4432
- path: query.size > 0 ? `/v1/models?${query}` : "/v1/models",
4539
+ path: query2.size > 0 ? `/v1/models?${query2}` : "/v1/models",
4433
4540
  method: "GET"
4434
4541
  };
4435
4542
  }
@@ -4444,12 +4551,12 @@ function desktopEndpoint(request) {
4444
4551
  return { path: `/v1/agents/${request.agentId}/upload`, method: "POST" };
4445
4552
  case "agent.publish": {
4446
4553
  assertPairedPublicationTarget(request);
4447
- const query = new URLSearchParams();
4554
+ const query2 = new URLSearchParams();
4448
4555
  if (request.visibility === "org" && request.tenantId) {
4449
- query.set("visibility", "org");
4450
- query.set("tenant_id", request.tenantId);
4556
+ query2.set("visibility", "org");
4557
+ query2.set("tenant_id", request.tenantId);
4451
4558
  }
4452
- const search = query.toString();
4559
+ const search = query2.toString();
4453
4560
  return {
4454
4561
  path: `/v1/agents/${request.agentId}/publish${search ? `?${search}` : ""}`,
4455
4562
  method: "POST",
@@ -4466,12 +4573,12 @@ function desktopEndpoint(request) {
4466
4573
  }
4467
4574
  case "agent.republish": {
4468
4575
  assertPairedPublicationTarget(request);
4469
- const query = new URLSearchParams();
4576
+ const query2 = new URLSearchParams();
4470
4577
  if (request.visibility === "org" && request.tenantId) {
4471
- query.set("visibility", "org");
4472
- query.set("tenant_id", request.tenantId);
4578
+ query2.set("visibility", "org");
4579
+ query2.set("tenant_id", request.tenantId);
4473
4580
  }
4474
- const search = query.toString();
4581
+ const search = query2.toString();
4475
4582
  return {
4476
4583
  path: `/v1/agents/${request.agentId}/publish${search ? `?${search}` : ""}`,
4477
4584
  method: "PUT",
@@ -4485,49 +4592,49 @@ function desktopEndpoint(request) {
4485
4592
  }
4486
4593
  case "team.publish": {
4487
4594
  assertPairedPublicationTarget(request);
4488
- const query = new URLSearchParams();
4595
+ const query2 = new URLSearchParams();
4489
4596
  if (request.visibility === "org" && request.tenantId) {
4490
- query.set("visibility", "org");
4491
- query.set("tenant_id", request.tenantId);
4597
+ query2.set("visibility", "org");
4598
+ query2.set("tenant_id", request.tenantId);
4492
4599
  }
4493
4600
  return {
4494
- path: `/v1/teams/${request.teamId}/publish${query.size ? `?${query}` : ""}`,
4601
+ path: `/v1/teams/${request.teamId}/publish${query2.size ? `?${query2}` : ""}`,
4495
4602
  method: "POST",
4496
4603
  body: publicationPolicyBody(request)
4497
4604
  };
4498
4605
  }
4499
4606
  case "team.pull": {
4500
- const query = new URLSearchParams();
4501
- if (request.tenantId) query.set("tenant_id", request.tenantId);
4502
- const search = query.toString();
4607
+ const query2 = new URLSearchParams();
4608
+ if (request.tenantId) query2.set("tenant_id", request.tenantId);
4609
+ const search = query2.toString();
4503
4610
  return {
4504
4611
  path: `/v1/teams/pull/${request.teamId}${search ? `?${search}` : ""}`,
4505
4612
  method: "GET"
4506
4613
  };
4507
4614
  }
4508
4615
  case "agent.nameAvailability": {
4509
- const query = new URLSearchParams({ name: request.name });
4510
- return { path: `/v1/agent-name-availability?${query}`, method: "GET" };
4616
+ const query2 = new URLSearchParams({ name: request.name });
4617
+ return { path: `/v1/agent-name-availability?${query2}`, method: "GET" };
4511
4618
  }
4512
4619
  case "legacy.agents.list": {
4513
- const query = new URLSearchParams({
4620
+ const query2 = new URLSearchParams({
4514
4621
  page: String(request.page ?? 1),
4515
4622
  per_page: String(request.perPage ?? 10)
4516
4623
  });
4517
- if (request.name) query.set("name", request.name);
4518
- if (request.sort) query.set("sort", request.sort);
4519
- if (request.direction) query.set("direction", request.direction);
4520
- return { path: `/v1/agents?${query}`, method: "GET" };
4624
+ if (request.name) query2.set("name", request.name);
4625
+ if (request.sort) query2.set("sort", request.sort);
4626
+ if (request.direction) query2.set("direction", request.direction);
4627
+ return { path: `/v1/agents?${query2}`, method: "GET" };
4521
4628
  }
4522
4629
  case "legacy.agent.get":
4523
4630
  return { path: `/v1/agents/${request.agentId}`, method: "GET" };
4524
4631
  case "legacy.agent.history": {
4525
- const query = new URLSearchParams({
4632
+ const query2 = new URLSearchParams({
4526
4633
  page: String(request.page ?? 1),
4527
4634
  per_page: String(request.perPage ?? 10)
4528
4635
  });
4529
4636
  return {
4530
- path: `/v1/agents/${request.agentId}/history?${query}`,
4637
+ path: `/v1/agents/${request.agentId}/history?${query2}`,
4531
4638
  method: "GET"
4532
4639
  };
4533
4640
  }
@@ -4537,13 +4644,13 @@ function desktopEndpoint(request) {
4537
4644
  * what the backend's models declare.
4538
4645
  */
4539
4646
  case "wakes.list": {
4540
- const query = new URLSearchParams();
4647
+ const query2 = new URLSearchParams();
4541
4648
  if (request.limit !== void 0)
4542
- query.set("limit", String(request.limit));
4649
+ query2.set("limit", String(request.limit));
4543
4650
  if (request.includeDormant !== void 0)
4544
- query.set("include_dormant", String(request.includeDormant));
4651
+ query2.set("include_dormant", String(request.includeDormant));
4545
4652
  return {
4546
- path: query.size > 0 ? `/v1/desktop/wakes?${query}` : "/v1/desktop/wakes",
4653
+ path: query2.size > 0 ? `/v1/desktop/wakes?${query2}` : "/v1/desktop/wakes",
4547
4654
  method: "GET"
4548
4655
  };
4549
4656
  }
@@ -4600,30 +4707,30 @@ function desktopEndpoint(request) {
4600
4707
  method: "POST"
4601
4708
  };
4602
4709
  case "legacy.jobs.list": {
4603
- const query = new URLSearchParams();
4604
- if (request.agentId) query.set("agent_id", request.agentId);
4605
- if (request.status) query.set("status", request.status);
4710
+ const query2 = new URLSearchParams();
4711
+ if (request.agentId) query2.set("agent_id", request.agentId);
4712
+ if (request.status) query2.set("status", request.status);
4606
4713
  return {
4607
- path: query.size > 0 ? `/v1/jobs?${query}` : "/v1/jobs",
4714
+ path: query2.size > 0 ? `/v1/jobs?${query2}` : "/v1/jobs",
4608
4715
  method: "GET"
4609
4716
  };
4610
4717
  }
4611
4718
  case "legacy.job.get":
4612
4719
  return { path: `/v1/jobs/${request.jobId}`, method: "GET" };
4613
4720
  case "legacy.schedules.list": {
4614
- const query = new URLSearchParams({
4721
+ const query2 = new URLSearchParams({
4615
4722
  page: String(request.page ?? 1),
4616
4723
  per_page: String(request.perPage ?? 10)
4617
4724
  });
4618
- return { path: `/v1/schedules?${query}`, method: "GET" };
4725
+ return { path: `/v1/schedules?${query2}`, method: "GET" };
4619
4726
  }
4620
4727
  case "legacy.agent.schedules.list": {
4621
- const query = new URLSearchParams({
4728
+ const query2 = new URLSearchParams({
4622
4729
  page: String(request.page ?? 1),
4623
4730
  per_page: String(request.perPage ?? 10)
4624
4731
  });
4625
4732
  return {
4626
- path: `/v1/agents/${request.agentId}/schedules?${query}`,
4733
+ path: `/v1/agents/${request.agentId}/schedules?${query2}`,
4627
4734
  method: "GET"
4628
4735
  };
4629
4736
  }
@@ -4688,29 +4795,29 @@ function desktopEndpoint(request) {
4688
4795
  };
4689
4796
  }
4690
4797
  case "usage.get": {
4691
- const query = new URLSearchParams({
4798
+ const query2 = new URLSearchParams({
4692
4799
  live: String(request.live ?? false),
4693
4800
  refresh: String(request.refresh ?? false)
4694
4801
  });
4695
- if (request.provider) query.set("provider", request.provider);
4696
- return { path: `/v1/desktop/usage?${query}`, method: "GET" };
4802
+ if (request.provider) query2.set("provider", request.provider);
4803
+ return { path: `/v1/desktop/usage?${query2}`, method: "GET" };
4697
4804
  }
4698
4805
  case "quota.notice": {
4699
- const query = new URLSearchParams({
4806
+ const query2 = new URLSearchParams({
4700
4807
  refresh: String(request.refresh ?? false)
4701
4808
  });
4702
- if (request.provider) query.set("provider", request.provider);
4703
- if (request.model) query.set("model", request.model);
4704
- return { path: `/v1/desktop/quota-notice?${query}`, method: "GET" };
4809
+ if (request.provider) query2.set("provider", request.provider);
4810
+ if (request.model) query2.set("model", request.model);
4811
+ return { path: `/v1/desktop/quota-notice?${query2}`, method: "GET" };
4705
4812
  }
4706
4813
  case "analytics.get": {
4707
- const query = new URLSearchParams({ days: String(request.days ?? 30) });
4708
- if (request.sessionId) query.set("session_id", request.sessionId);
4814
+ const query2 = new URLSearchParams({ days: String(request.days ?? 30) });
4815
+ if (request.sessionId) query2.set("session_id", request.sessionId);
4709
4816
  if (request.sinceMs !== void 0)
4710
- query.set("since_ms", String(request.sinceMs));
4817
+ query2.set("since_ms", String(request.sinceMs));
4711
4818
  if (request.untilMs !== void 0)
4712
- query.set("until_ms", String(request.untilMs));
4713
- return { path: `/v1/desktop/analytics?${query}`, method: "GET" };
4819
+ query2.set("until_ms", String(request.untilMs));
4820
+ return { path: `/v1/desktop/analytics?${query2}`, method: "GET" };
4714
4821
  }
4715
4822
  /*
4716
4823
  * `/analytics/models`, NOT `/analytics?models=1`.
@@ -4723,31 +4830,31 @@ function desktopEndpoint(request) {
4723
4830
  * the server resolves one window from either.
4724
4831
  */
4725
4832
  case "analytics.models": {
4726
- const query = new URLSearchParams({ days: String(request.days ?? 30) });
4727
- if (request.sessionId) query.set("session_id", request.sessionId);
4833
+ const query2 = new URLSearchParams({ days: String(request.days ?? 30) });
4834
+ if (request.sessionId) query2.set("session_id", request.sessionId);
4728
4835
  if (request.sinceMs !== void 0)
4729
- query.set("since_ms", String(request.sinceMs));
4836
+ query2.set("since_ms", String(request.sinceMs));
4730
4837
  if (request.untilMs !== void 0)
4731
- query.set("until_ms", String(request.untilMs));
4732
- return { path: `/v1/desktop/analytics/models?${query}`, method: "GET" };
4838
+ query2.set("until_ms", String(request.untilMs));
4839
+ return { path: `/v1/desktop/analytics/models?${query2}`, method: "GET" };
4733
4840
  }
4734
4841
  case "info.get":
4735
4842
  return { path: "/v1/desktop/info", method: "GET" };
4736
4843
  case "sessions.report": {
4737
- const query = new URLSearchParams({
4844
+ const query2 = new URLSearchParams({
4738
4845
  recent_limit: String(request.recentLimit ?? 12)
4739
4846
  });
4740
4847
  return {
4741
- path: `/v1/desktop/sessions/${request.sessionId}/report?${query}`,
4848
+ path: `/v1/desktop/sessions/${request.sessionId}/report?${query2}`,
4742
4849
  method: "GET"
4743
4850
  };
4744
4851
  }
4745
4852
  case "skills.list": {
4746
- const query = new URLSearchParams();
4747
- if (request.sessionId) query.set("session_id", request.sessionId);
4748
- if (request.cwd) query.set("cwd", request.cwd);
4749
- if (request.name) query.set("name", request.name);
4750
- const encoded = query.toString();
4853
+ const query2 = new URLSearchParams();
4854
+ if (request.sessionId) query2.set("session_id", request.sessionId);
4855
+ if (request.cwd) query2.set("cwd", request.cwd);
4856
+ if (request.name) query2.set("name", request.name);
4857
+ const encoded = query2.toString();
4751
4858
  return {
4752
4859
  path: `/v1/desktop/skills${encoded ? `?${encoded}` : ""}`,
4753
4860
  method: "GET"
@@ -4852,10 +4959,10 @@ function desktopEndpoint(request) {
4852
4959
  body: request.control
4853
4960
  };
4854
4961
  case "mcp.catalog": {
4855
- const query = new URLSearchParams();
4856
- if (request.cwd) query.set("cwd", request.cwd);
4857
- if (request.sessionId) query.set("session_id", request.sessionId);
4858
- const suffix = query.toString();
4962
+ const query2 = new URLSearchParams();
4963
+ if (request.cwd) query2.set("cwd", request.cwd);
4964
+ if (request.sessionId) query2.set("session_id", request.sessionId);
4965
+ const suffix = query2.toString();
4859
4966
  return {
4860
4967
  path: suffix ? `/v1/desktop/mcp?${suffix}` : "/v1/desktop/mcp",
4861
4968
  method: "GET"
@@ -5003,14 +5110,14 @@ function desktopEndpoint(request) {
5003
5110
  }
5004
5111
  };
5005
5112
  case "team_bridge.conversations.list": {
5006
- const query = new URLSearchParams();
5007
- if (request.scope === "account") query.set("scope", "account");
5008
- if (request.tenant_id) query.set("tenant_id", request.tenant_id);
5113
+ const query2 = new URLSearchParams();
5114
+ if (request.scope === "account") query2.set("scope", "account");
5115
+ if (request.tenant_id) query2.set("tenant_id", request.tenant_id);
5009
5116
  if (request.limit !== void 0)
5010
- query.set("limit", String(request.limit));
5011
- if (request.cursor) query.set("cursor", request.cursor);
5012
- if (request.before) query.set("before", request.before);
5013
- const suffix = query.size > 0 ? `?${query.toString()}` : "";
5117
+ query2.set("limit", String(request.limit));
5118
+ if (request.cursor) query2.set("cursor", request.cursor);
5119
+ if (request.before) query2.set("before", request.before);
5120
+ const suffix = query2.size > 0 ? `?${query2.toString()}` : "";
5014
5121
  return {
5015
5122
  path: `/v1/desktop/team-bridge/conversations${suffix}`,
5016
5123
  method: "GET"
@@ -5026,6 +5133,7 @@ function desktopEndpoint(request) {
5026
5133
  path: "/v1/desktop/team-bridge/conversations",
5027
5134
  method: "POST",
5028
5135
  body: {
5136
+ ...request.expected_account_id ? { expected_account_id: request.expected_account_id } : {},
5029
5137
  ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
5030
5138
  kind: request.kind,
5031
5139
  ...request.name ? { name: request.name } : {},
@@ -5037,14 +5145,25 @@ function desktopEndpoint(request) {
5037
5145
  };
5038
5146
  case "team_bridge.conversations.lifecycle":
5039
5147
  return {
5040
- path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/${request.action}`,
5041
- method: "POST"
5148
+ path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/${request.action}${request.action === "join" ? "" : conversationOwnerQuery(request)}`,
5149
+ method: "POST",
5150
+ ...request.action === "join" && (request.tenant_id || request.expected_account_id) ? {
5151
+ body: {
5152
+ ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
5153
+ ...request.expected_account_id ? { expected_account_id: request.expected_account_id } : {}
5154
+ }
5155
+ } : {}
5042
5156
  };
5043
5157
  case "team_bridge.conversations.join":
5044
5158
  return {
5045
5159
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/join`,
5046
5160
  method: "POST",
5047
- ...request.tenant_id ? { body: { tenant_id: request.tenant_id } } : {}
5161
+ ...request.tenant_id || request.expected_account_id ? {
5162
+ body: {
5163
+ ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
5164
+ ...request.expected_account_id ? { expected_account_id: request.expected_account_id } : {}
5165
+ }
5166
+ } : {}
5048
5167
  };
5049
5168
  case "team_bridge.conversations.notify":
5050
5169
  return {
@@ -5053,14 +5172,17 @@ function desktopEndpoint(request) {
5053
5172
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/read`,
5054
5173
  method: "POST",
5055
5174
  body: {
5175
+ ...request.expected_account_id ? { expected_account_id: request.expected_account_id } : {},
5056
5176
  ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
5057
5177
  notify: request.notify
5058
5178
  }
5059
5179
  };
5060
5180
  case "team_bridge.conversations.members.add": {
5061
- const query = new URLSearchParams();
5062
- if (request.tenant_id) query.set("tenant_id", request.tenant_id);
5063
- const suffix = query.size > 0 ? `?${query.toString()}` : "";
5181
+ const query2 = new URLSearchParams();
5182
+ if (request.tenant_id) query2.set("tenant_id", request.tenant_id);
5183
+ if (request.expected_account_id)
5184
+ query2.set("expected_account_id", request.expected_account_id);
5185
+ const suffix = query2.size > 0 ? `?${query2.toString()}` : "";
5064
5186
  return {
5065
5187
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/members${suffix}`,
5066
5188
  method: "POST",
@@ -5069,7 +5191,7 @@ function desktopEndpoint(request) {
5069
5191
  }
5070
5192
  case "team_bridge.conversations.members.remove":
5071
5193
  return {
5072
- path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/members/${encodeURIComponent(request.account_id)}`,
5194
+ path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/members/${encodeURIComponent(request.account_id)}${conversationOwnerQuery(request)}`,
5073
5195
  method: "DELETE"
5074
5196
  };
5075
5197
  case "team_bridge.conversations.consent":
@@ -5077,20 +5199,21 @@ function desktopEndpoint(request) {
5077
5199
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/members/${encodeURIComponent(request.account_id)}/consent`,
5078
5200
  method: "POST",
5079
5201
  body: {
5202
+ ...request.expected_account_id ? { expected_account_id: request.expected_account_id } : {},
5080
5203
  ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
5081
5204
  decision: request.decision,
5082
5205
  consent_version: request.consent_version
5083
5206
  }
5084
5207
  };
5085
5208
  case "team_bridge.messages.list": {
5086
- const query = new URLSearchParams();
5087
- if (request.tenant_id) query.set("tenant_id", request.tenant_id);
5209
+ const query2 = new URLSearchParams();
5210
+ if (request.tenant_id) query2.set("tenant_id", request.tenant_id);
5088
5211
  if (request.limit !== void 0)
5089
- query.set("limit", String(request.limit));
5212
+ query2.set("limit", String(request.limit));
5090
5213
  if (request.before_seq !== void 0)
5091
- query.set("before_seq", String(request.before_seq));
5092
- if (request.endpoint_id) query.set("endpoint_id", request.endpoint_id);
5093
- const suffix = query.size > 0 ? `?${query.toString()}` : "";
5214
+ query2.set("before_seq", String(request.before_seq));
5215
+ if (request.endpoint_id) query2.set("endpoint_id", request.endpoint_id);
5216
+ const suffix = query2.size > 0 ? `?${query2.toString()}` : "";
5094
5217
  return {
5095
5218
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/messages${suffix}`,
5096
5219
  method: "GET"
@@ -5112,6 +5235,7 @@ function desktopEndpoint(request) {
5112
5235
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/messages`,
5113
5236
  method: "POST",
5114
5237
  body: {
5238
+ ...request.expected_account_id ? { expected_account_id: request.expected_account_id } : {},
5115
5239
  ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
5116
5240
  client_ref: request.client_ref,
5117
5241
  body: request.body,
@@ -5120,13 +5244,13 @@ function desktopEndpoint(request) {
5120
5244
  }
5121
5245
  };
5122
5246
  case "team_bridge.messages.replies": {
5123
- const query = new URLSearchParams();
5124
- if (request.tenant_id) query.set("tenant_id", request.tenant_id);
5247
+ const query2 = new URLSearchParams();
5248
+ if (request.tenant_id) query2.set("tenant_id", request.tenant_id);
5125
5249
  if (request.limit !== void 0)
5126
- query.set("limit", String(request.limit));
5250
+ query2.set("limit", String(request.limit));
5127
5251
  if (request.after_seq !== void 0)
5128
- query.set("after_seq", String(request.after_seq));
5129
- const suffix = query.size > 0 ? `?${query.toString()}` : "";
5252
+ query2.set("after_seq", String(request.after_seq));
5253
+ const suffix = query2.size > 0 ? `?${query2.toString()}` : "";
5130
5254
  return {
5131
5255
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/messages/${encodeURIComponent(request.message_id)}/replies${suffix}`,
5132
5256
  method: "GET"
@@ -5137,16 +5261,17 @@ function desktopEndpoint(request) {
5137
5261
  path: `/v1/desktop/team-bridge/conversations/${encodeURIComponent(request.conversation_id)}/read`,
5138
5262
  method: "POST",
5139
5263
  body: {
5264
+ ...request.expected_account_id ? { expected_account_id: request.expected_account_id } : {},
5140
5265
  ...request.tenant_id ? { tenant_id: request.tenant_id } : {},
5141
5266
  ...request.seq === void 0 ? {} : { seq: request.seq },
5142
5267
  ...request.muted === void 0 ? {} : { muted: request.muted }
5143
5268
  }
5144
5269
  };
5145
5270
  case "team_bridge.unread": {
5146
- const query = new URLSearchParams();
5147
- if (request.scope === "account") query.set("scope", "account");
5148
- else if (request.tenant_id) query.set("tenant_id", request.tenant_id);
5149
- const suffix = query.size > 0 ? `?${query.toString()}` : "";
5271
+ const query2 = new URLSearchParams();
5272
+ if (request.scope === "account") query2.set("scope", "account");
5273
+ else if (request.tenant_id) query2.set("tenant_id", request.tenant_id);
5274
+ const suffix = query2.size > 0 ? `?${query2.toString()}` : "";
5150
5275
  return {
5151
5276
  path: `/v1/desktop/team-bridge/unread${suffix}`,
5152
5277
  method: "GET"
@@ -5174,9 +5299,9 @@ function desktopEndpoint(request) {
5174
5299
  * op would emit for a missing value.
5175
5300
  */
5176
5301
  case "team_bridge.status": {
5177
- const query = new URLSearchParams();
5178
- if (request.boundary) query.set("boundary", request.boundary);
5179
- const suffix = query.size > 0 ? `?${query.toString()}` : "";
5302
+ const query2 = new URLSearchParams();
5303
+ if (request.boundary) query2.set("boundary", request.boundary);
5304
+ const suffix = query2.size > 0 ? `?${query2.toString()}` : "";
5180
5305
  return {
5181
5306
  path: `/v1/desktop/team-bridge/status${suffix}`,
5182
5307
  method: "GET"
@@ -5235,29 +5360,29 @@ function desktopEndpoint(request) {
5235
5360
  * `op` because that key already names the desktop operation.
5236
5361
  */
5237
5362
  case "library.list": {
5238
- const query = new URLSearchParams();
5239
- if (request.scope) query.set("scope", request.scope);
5240
- if (request.q) query.set("q", request.q);
5241
- if (request.category) query.set("category", request.category);
5242
- if (request.kind) query.set("kind", request.kind);
5243
- if (request.origin_group) query.set("origin_group", request.origin_group);
5244
- if (request.origin) query.set("origin", request.origin);
5245
- if (request.tag) query.set("tag", request.tag);
5246
- if (request.tag_mode) query.set("tag_mode", request.tag_mode);
5363
+ const query2 = new URLSearchParams();
5364
+ if (request.scope) query2.set("scope", request.scope);
5365
+ if (request.q) query2.set("q", request.q);
5366
+ if (request.category) query2.set("category", request.category);
5367
+ if (request.kind) query2.set("kind", request.kind);
5368
+ if (request.origin_group) query2.set("origin_group", request.origin_group);
5369
+ if (request.origin) query2.set("origin", request.origin);
5370
+ if (request.tag) query2.set("tag", request.tag);
5371
+ if (request.tag_mode) query2.set("tag_mode", request.tag_mode);
5247
5372
  if (request.highlighted !== void 0)
5248
- query.set("highlighted", request.highlighted ? "1" : "0");
5373
+ query2.set("highlighted", request.highlighted ? "1" : "0");
5249
5374
  if (request.organized !== void 0)
5250
- query.set("organized", request.organized ? "1" : "0");
5375
+ query2.set("organized", request.organized ? "1" : "0");
5251
5376
  if (request.favorite !== void 0)
5252
- query.set("favorite", request.favorite ? "1" : "0");
5253
- if (request.session) query.set("session", request.session);
5254
- if (request.session_id) query.set("session_id", request.session_id);
5255
- if (request.sort) query.set("sort", request.sort);
5256
- if (request.order) query.set("order", request.order);
5377
+ query2.set("favorite", request.favorite ? "1" : "0");
5378
+ if (request.session) query2.set("session", request.session);
5379
+ if (request.session_id) query2.set("session_id", request.session_id);
5380
+ if (request.sort) query2.set("sort", request.sort);
5381
+ if (request.order) query2.set("order", request.order);
5257
5382
  if (request.limit !== void 0)
5258
- query.set("limit", String(request.limit));
5259
- if (request.cursor) query.set("cursor", request.cursor);
5260
- const suffix = query.toString();
5383
+ query2.set("limit", String(request.limit));
5384
+ if (request.cursor) query2.set("cursor", request.cursor);
5385
+ const suffix = query2.toString();
5261
5386
  return {
5262
5387
  path: `/v1/desktop/library/assets${suffix ? `?${suffix}` : ""}`,
5263
5388
  method: "GET"
@@ -5418,12 +5543,12 @@ function desktopEndpoint(request) {
5418
5543
  method: "POST"
5419
5544
  };
5420
5545
  case "projects.search": {
5421
- const query = new URLSearchParams({
5546
+ const query2 = new URLSearchParams({
5422
5547
  q: request.q,
5423
5548
  limit: String(request.limit ?? PROJECTS_SEARCH_DEFAULT_LIMIT)
5424
5549
  });
5425
5550
  return {
5426
- path: `/v1/desktop/projects/search?${query}`,
5551
+ path: `/v1/desktop/projects/search?${query2}`,
5427
5552
  method: "GET"
5428
5553
  };
5429
5554
  }
@@ -5823,6 +5948,187 @@ function windowGlassArgument(state2, scheme) {
5823
5948
  const { settings, capability } = state2;
5824
5949
  return `${WINDOW_GLASS_ARG}=${capability.mechanism}:${settings.coverage}:${settings.intensity}:${settings.material}:${scheme}`;
5825
5950
  }
5951
+ const UPLOAD_DEADLINE_MS = 5 * 6e4;
5952
+ const DIGEST_SHAPE = /^[0-9a-f]{32}$/;
5953
+ const PATH_SEPARATOR = /[/\\]/;
5954
+ const basenameOf = (filePath) => filePath.split(PATH_SEPARATOR).pop() || filePath;
5955
+ function bytesFromDataUrl(dataUrl) {
5956
+ const comma = dataUrl.indexOf(",");
5957
+ if (comma < 0 || !dataUrl.startsWith("data:")) return null;
5958
+ const header = dataUrl.slice(5, comma);
5959
+ const payload = dataUrl.slice(comma + 1);
5960
+ try {
5961
+ if (header.endsWith(";base64"))
5962
+ return new Uint8Array(Buffer.from(payload, "base64"));
5963
+ return new Uint8Array(Buffer.from(decodeURIComponent(payload), "utf8"));
5964
+ } catch {
5965
+ return null;
5966
+ }
5967
+ }
5968
+ function readRecord(payload) {
5969
+ if (payload === null || typeof payload !== "object") return null;
5970
+ const body = payload;
5971
+ const inner = body.result;
5972
+ if (inner !== null && typeof inner === "object")
5973
+ return inner;
5974
+ return body;
5975
+ }
5976
+ function toAttachment(payload) {
5977
+ const record = readRecord(payload);
5978
+ if (!record) return null;
5979
+ const digest = record.digest;
5980
+ const mime = record.mime_type;
5981
+ const bytes = record.bytes;
5982
+ if (typeof digest !== "string" || !DIGEST_SHAPE.test(digest)) return null;
5983
+ if (typeof mime !== "string" || mime.length === 0) return null;
5984
+ if (typeof bytes !== "number" || !Number.isFinite(bytes) || bytes < 0)
5985
+ return null;
5986
+ return {
5987
+ digest,
5988
+ name: typeof record.name === "string" ? record.name : null,
5989
+ mime_type: mime,
5990
+ bytes,
5991
+ path: typeof record.path === "string" ? record.path : null,
5992
+ stored_path: typeof record.stored_path === "string" ? record.stored_path : null
5993
+ };
5994
+ }
5995
+ function refusalDetail(payload) {
5996
+ if (payload === null || typeof payload !== "object") return null;
5997
+ const detail = payload.detail;
5998
+ if (typeof detail === "string" && detail.trim()) return detail;
5999
+ if (detail !== null && typeof detail === "object") {
6000
+ const message = detail.message;
6001
+ if (typeof message === "string" && message.trim()) return message;
6002
+ }
6003
+ return null;
6004
+ }
6005
+ async function uploadAttachment(input) {
6006
+ const cap = input.maxBytes ?? MAX_ATTACHMENT_UPLOAD_BYTES;
6007
+ const source = input.source;
6008
+ const displayName = source.name?.trim() || (source.kind === "path" ? basenameOf(source.path) : "pasted-file");
6009
+ let originPath = null;
6010
+ let size;
6011
+ if (source.kind === "path") {
6012
+ const filePath = source.path;
6013
+ originPath = filePath;
6014
+ try {
6015
+ const stat = fs.statSync(filePath, { throwIfNoEntry: false });
6016
+ if (!stat) {
6017
+ return {
6018
+ success: false,
6019
+ code: "not-found",
6020
+ error: `No file at ${filePath}`
6021
+ };
6022
+ }
6023
+ if (!stat.isFile()) {
6024
+ return {
6025
+ success: false,
6026
+ code: "not-a-file",
6027
+ error: `${filePath} is not a regular file`
6028
+ };
6029
+ }
6030
+ size = stat.size;
6031
+ } catch (error) {
6032
+ return {
6033
+ success: false,
6034
+ code: "unreadable",
6035
+ error: error instanceof Error ? error.message : String(error)
6036
+ };
6037
+ }
6038
+ } else {
6039
+ const decoded = bytesFromDataUrl(source.dataUrl);
6040
+ if (!decoded) {
6041
+ return {
6042
+ success: false,
6043
+ code: "unreadable",
6044
+ error: `${displayName} did not carry usable bytes`
6045
+ };
6046
+ }
6047
+ size = decoded.byteLength;
6048
+ }
6049
+ if (size === 0) {
6050
+ return {
6051
+ success: false,
6052
+ code: "refused",
6053
+ error: `${displayName} is empty`,
6054
+ sizeBytes: 0
6055
+ };
6056
+ }
6057
+ if (size > cap) {
6058
+ return {
6059
+ success: false,
6060
+ code: "too-large",
6061
+ error: `${displayName} is ${size} bytes, over the ${cap}-byte attachment limit`,
6062
+ sizeBytes: size
6063
+ };
6064
+ }
6065
+ let bytes;
6066
+ try {
6067
+ bytes = source.kind === "path" ? new Uint8Array(fs.readFileSync(source.path)) : bytesFromDataUrl(source.dataUrl);
6068
+ } catch (error) {
6069
+ return {
6070
+ success: false,
6071
+ code: "unreadable",
6072
+ error: error instanceof Error ? error.message : String(error)
6073
+ };
6074
+ }
6075
+ const form = new FormData();
6076
+ form.append(
6077
+ "file",
6078
+ new Blob([bytes], {
6079
+ type: "application/octet-stream"
6080
+ }),
6081
+ displayName
6082
+ );
6083
+ form.append("name", displayName);
6084
+ if (originPath) form.append("origin_path", originPath);
6085
+ let response;
6086
+ try {
6087
+ const doFetch = input.fetchImpl ?? fetch;
6088
+ response = await doFetch(
6089
+ new URL("/v1/desktop/attachments", input.backendUrl),
6090
+ {
6091
+ method: "POST",
6092
+ headers: {
6093
+ Accept: "application/json",
6094
+ ...input.token ? { Authorization: `Bearer ${input.token}` } : {}
6095
+ },
6096
+ body: form,
6097
+ redirect: "error",
6098
+ signal: AbortSignal.timeout(UPLOAD_DEADLINE_MS)
6099
+ }
6100
+ );
6101
+ } catch (error) {
6102
+ return {
6103
+ success: false,
6104
+ code: "unreachable",
6105
+ error: error instanceof Error ? error.message : "The daemon did not answer the upload."
6106
+ };
6107
+ }
6108
+ let payload = null;
6109
+ try {
6110
+ payload = await response.json();
6111
+ } catch {
6112
+ }
6113
+ if (response.status < 200 || response.status >= 300) {
6114
+ return {
6115
+ success: false,
6116
+ code: "refused",
6117
+ error: refusalDetail(payload) ?? `The daemon refused the upload (HTTP ${response.status}).`,
6118
+ sizeBytes: size
6119
+ };
6120
+ }
6121
+ const attachment = toAttachment(payload);
6122
+ if (!attachment) {
6123
+ return {
6124
+ success: false,
6125
+ code: "refused",
6126
+ error: `${displayName} was uploaded but the daemon's answer carried no attachment record.`,
6127
+ sizeBytes: size
6128
+ };
6129
+ }
6130
+ return { success: true, attachment };
6131
+ }
5826
6132
  const SERVE_RUN_DIRNAME = "run/serve";
5827
6133
  const REAPED_DIRNAME = "reaped";
5828
6134
  const REAPED_MAX_FILES = 200;
@@ -9422,25 +9728,25 @@ class DesktopStreamRelay {
9422
9728
  const streamId = node_crypto.randomBytes(16).toString("hex");
9423
9729
  const controller = new AbortController();
9424
9730
  this.streams.set(streamId, controller);
9425
- const query = new URLSearchParams();
9426
- if (args.epoch) query.set("epoch", args.epoch);
9731
+ const query2 = new URLSearchParams();
9732
+ if (args.epoch) query2.set("epoch", args.epoch);
9427
9733
  if (args.afterSeq !== void 0)
9428
- query.set("after_seq", String(args.afterSeq));
9429
- query.set("frontend_replace", "1");
9430
- if (args.openFrame === true) query.set("open_frame", "1");
9734
+ query2.set("after_seq", String(args.afterSeq));
9735
+ query2.set("frontend_replace", "1");
9736
+ if (args.openFrame === true) query2.set("open_frame", "1");
9431
9737
  if (args.journalOrder === true) {
9432
- query.set("journal_order", "1");
9738
+ query2.set("journal_order", "1");
9433
9739
  if (args.peerJournalSource === true)
9434
- query.set("peer_journal_source", "1");
9740
+ query2.set("peer_journal_source", "1");
9435
9741
  }
9436
- query.set("supplements", "1");
9742
+ query2.set("supplements", "1");
9437
9743
  if (args.journalOrder === true)
9438
9744
  appendJournalOrderIds(
9439
- query,
9745
+ query2,
9440
9746
  `/v1/desktop/sessions/${args.sessionId}/events`,
9441
9747
  args.orderIds ?? []
9442
9748
  );
9443
- const suffix = query.size > 0 ? `?${query}` : "";
9749
+ const suffix = query2.size > 0 ? `?${query2}` : "";
9444
9750
  const url = new URL(
9445
9751
  `/v1/desktop/sessions/${args.sessionId}/events${suffix}`,
9446
9752
  this.backendUrl
@@ -14694,6 +15000,20 @@ class BackendServiceManager {
14694
15000
  getBackendUrl() {
14695
15001
  return this.backendUrl;
14696
15002
  }
15003
+ /**
15004
+ * The bearer token this app presents to THAT daemon, or null while none has
15005
+ * been minted/adopted yet.
15006
+ *
15007
+ * Exposed for the one request that does not travel through the op plane: the
15008
+ * attachment UPLOAD (attachment contract v1 §1.1). It carries raw bytes in a
15009
+ * multipart body, which the op plane's JSON schemas cannot describe, so it
15010
+ * goes over `fetch` from main and has to present the same credential the
15011
+ * plane would. Kept a getter rather than a public field so the token's
15012
+ * lifecycle stays owned here (`mintDesktopToken`, adoption, persistence).
15013
+ */
15014
+ getDesktopToken() {
15015
+ return this.desktopToken;
15016
+ }
14697
15017
  /**
14698
15018
  * Check if the backend service is using an external backend
14699
15019
  * @returns True if using an external backend, false if we started our own
@@ -14839,12 +15159,12 @@ const REASON_LIMIT = 240;
14839
15159
  function installFailureReason(stdout, stderr) {
14840
15160
  const lines = `${stderr}
14841
15161
  ${stdout}`.split(/\r?\n/);
14842
- const pick = (accept) => {
15162
+ const pick = (accept2) => {
14843
15163
  for (let index = lines.length - 1; index >= 0; index -= 1) {
14844
15164
  const line = lines[index].trim();
14845
15165
  if (line.length === 0) continue;
14846
15166
  if (NOISE.some((pattern) => pattern.test(line))) continue;
14847
- if (!accept(line)) continue;
15167
+ if (!accept2(line)) continue;
14848
15168
  return line.length > REASON_LIMIT ? `${line.slice(0, REASON_LIMIT - 1).trimEnd()}…` : line;
14849
15169
  }
14850
15170
  return null;
@@ -16725,6 +17045,10 @@ const METHODS$1 = [
16725
17045
  "styles",
16726
17046
  "hit_test",
16727
17047
  "ancestors",
17048
+ // The read-only DOM query: CSS in, element descriptors out. A read by
17049
+ // construction (no write, no navigation, no caller-supplied code), served by
17050
+ // every non-cmux host like the structured reads above.
17051
+ "query",
16728
17052
  "request_access",
16729
17053
  "await_access",
16730
17054
  "cancel_access",
@@ -16789,6 +17113,7 @@ const HOST_CAPABILITIES = [
16789
17113
  "styles",
16790
17114
  "hit_test",
16791
17115
  "ancestors",
17116
+ "query",
16792
17117
  "download",
16793
17118
  "upload"
16794
17119
  ];
@@ -28089,7 +28414,7 @@ function nativeCapacityProbe(trustedPython) {
28089
28414
  let ptyMax = null;
28090
28415
  let stopped = false;
28091
28416
  const children = /* @__PURE__ */ new Set();
28092
- function readNumber(executable, args, accept) {
28417
+ function readNumber(executable, args, accept2) {
28093
28418
  const child = node_child_process.execFile(
28094
28419
  executable,
28095
28420
  args,
@@ -28104,7 +28429,7 @@ function nativeCapacityProbe(trustedPython) {
28104
28429
  children.delete(child);
28105
28430
  if (stopped || error) return;
28106
28431
  const value = Number(stdout.trim());
28107
- if (Number.isSafeInteger(value) && value > 0) accept(value);
28432
+ if (Number.isSafeInteger(value) && value > 0) accept2(value);
28108
28433
  }
28109
28434
  );
28110
28435
  children.add(child);
@@ -35690,6 +36015,34 @@ const enUiChatAnalytics = {
35690
36015
  "ui.chat.analytics.toolbar.refreshing": "Refreshing",
35691
36016
  "ui.chat.analytics.toolbar.this_session_only": "This session only"
35692
36017
  };
36018
+ const enUiChatAsks = {
36019
+ "ui.chat.asks.context.messages_since": "{count, plural, one {# message from you since this was asked} other {# messages from you since this was asked}} (last message {time})",
36020
+ "ui.chat.asks.dismiss.answered_in_chat.note": "you already replied in the conversation",
36021
+ "ui.chat.asks.dismiss.answered_in_chat.title": "Answered in chat",
36022
+ "ui.chat.asks.dismiss.cancel": "Cancel",
36023
+ "ui.chat.asks.dismiss.heading": "Dismiss this ask",
36024
+ "ui.chat.asks.dismiss.in_flight": "Dismissing…",
36025
+ "ui.chat.asks.dismiss.moot.note": "stop waiting on it",
36026
+ "ui.chat.asks.dismiss.moot.title": "No longer needed",
36027
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "Dismissed — settled in chat",
36028
+ "ui.chat.asks.dismiss.receipt.moot": "Dismissed — no longer needed",
36029
+ "ui.chat.asks.dismiss.refused": "Couldn't dismiss this ask — nothing was sent.",
36030
+ "ui.chat.asks.dismiss.trigger": "Dismiss…"
36031
+ };
36032
+ const enUiChatAttachments = {
36033
+ "ui.chat.attachments.pending_caption": "Not sent yet",
36034
+ "ui.chat.attachments.file_meta": "{type} · {size}",
36035
+ "ui.chat.attachments.file_missing": "Moved or deleted",
36036
+ "ui.chat.attachments.file_retained": "Still retained in the attachment store ({digest})",
36037
+ "ui.chat.attachments.file_unnamed": "Attached file",
36038
+ "ui.chat.attachments.copy_path": "Copy path",
36039
+ "ui.chat.attachments.path_copied": "Path copied",
36040
+ "ui.chat.attachments.file_missing_recovery": "The file is no longer at this path.",
36041
+ "ui.chat.attachments.file_type_unknown": "File",
36042
+ "ui.chat.attachments.copy_saved_path": "Copy saved copy path",
36043
+ "ui.chat.attachments.saved_path_copied": "Saved copy path copied",
36044
+ "ui.chat.attachments.saved_copy_label": "Saved copy"
36045
+ };
35693
36046
  const enUiChatCanonical = {
35694
36047
  "ui.chat.canonical.sound.batch_attachment_unavailable": "Audio attachment unavailable.",
35695
36048
  "ui.chat.canonical.sound.player_failed": "This sound could not be played",
@@ -39470,6 +39823,34 @@ const esUiCanvas = {
39470
39823
  "ui.canvas.files.prepareError": "No se pudo preparar una copia local para esta acción",
39471
39824
  "ui.canvas.files.pathCopyNote": "Las acciones de abrir, carpeta y ruta crean una copia local en el scratchpad de esta conversación."
39472
39825
  };
39826
+ const esUiChatAsks = {
39827
+ "ui.chat.asks.context.messages_since": "{count, plural, one {# mensaje tuyo desde que se hizo esta consulta (último mensaje: {time})} many {# de mensajes tuyos desde que se hizo esta consulta (último mensaje: {time})} other {# mensajes tuyos desde que se hizo esta consulta (último mensaje: {time})}}",
39828
+ "ui.chat.asks.dismiss.answered_in_chat.note": "ya respondiste en la conversación",
39829
+ "ui.chat.asks.dismiss.answered_in_chat.title": "Respondida en la conversación",
39830
+ "ui.chat.asks.dismiss.cancel": "Cancelar",
39831
+ "ui.chat.asks.dismiss.heading": "Descartar esta consulta",
39832
+ "ui.chat.asks.dismiss.in_flight": "Descartando…",
39833
+ "ui.chat.asks.dismiss.moot.note": "el agente dejará de esperar respuesta",
39834
+ "ui.chat.asks.dismiss.moot.title": "Ya no hace falta",
39835
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "Descartada — resuelta en la conversación",
39836
+ "ui.chat.asks.dismiss.receipt.moot": "Descartada — ya no hace falta",
39837
+ "ui.chat.asks.dismiss.refused": "No se pudo descartar esta consulta: no se envió nada.",
39838
+ "ui.chat.asks.dismiss.trigger": "Descartar…"
39839
+ };
39840
+ const esUiChatAttachments = {
39841
+ "ui.chat.attachments.pending_caption": "Aún no enviado",
39842
+ "ui.chat.attachments.file_meta": "{type} · {size}",
39843
+ "ui.chat.attachments.file_missing": "Movido o eliminado",
39844
+ "ui.chat.attachments.file_retained": "Todavía se conserva en el almacén de adjuntos ({digest})",
39845
+ "ui.chat.attachments.file_unnamed": "Archivo adjunto",
39846
+ "ui.chat.attachments.copy_path": "Copiar ruta",
39847
+ "ui.chat.attachments.path_copied": "Ruta copiada",
39848
+ "ui.chat.attachments.file_missing_recovery": "El archivo ya no está en esta ruta.",
39849
+ "ui.chat.attachments.file_type_unknown": "Archivo",
39850
+ "ui.chat.attachments.copy_saved_path": "Copiar ruta de la copia guardada",
39851
+ "ui.chat.attachments.saved_path_copied": "Ruta de la copia guardada copiada",
39852
+ "ui.chat.attachments.saved_copy_label": "Copia guardada"
39853
+ };
39473
39854
  const esUiChatComposer = {
39474
39855
  "ui.chat.composer.clear_message": "Borrar mensaje",
39475
39856
  "ui.chat.composer.message": "Mensaje",
@@ -43111,6 +43492,34 @@ const frUiCanvas = {
43111
43492
  "ui.canvas.files.prepareError": "Impossible de préparer une copie locale pour cette action",
43112
43493
  "ui.canvas.files.pathCopyNote": "Les actions Ouvrir, Dossier et Chemin créent une copie locale dans le dossier de travail de cette conversation."
43113
43494
  };
43495
+ const frUiChatAsks = {
43496
+ "ui.chat.asks.context.messages_since": "{count, plural, one {# message de votre part depuis cette demande (dernier message à {time})} many {# de messages de votre part depuis cette demande (dernier message à {time})} other {# messages de votre part depuis cette demande (dernier message à {time})}}",
43497
+ "ui.chat.asks.dismiss.answered_in_chat.note": "vous avez déjà répondu dans la conversation",
43498
+ "ui.chat.asks.dismiss.answered_in_chat.title": "Répondu dans la conversation",
43499
+ "ui.chat.asks.dismiss.cancel": "Annuler",
43500
+ "ui.chat.asks.dismiss.heading": "Ignorer cette demande",
43501
+ "ui.chat.asks.dismiss.in_flight": "En cours…",
43502
+ "ui.chat.asks.dismiss.moot.note": "l’agent n’attendra plus de réponse",
43503
+ "ui.chat.asks.dismiss.moot.title": "Plus nécessaire",
43504
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "Ignorée — réglée dans la conversation",
43505
+ "ui.chat.asks.dismiss.receipt.moot": "Ignorée — plus nécessaire",
43506
+ "ui.chat.asks.dismiss.refused": "Impossible d’ignorer cette demande : rien n’a été envoyé.",
43507
+ "ui.chat.asks.dismiss.trigger": "Ignorer…"
43508
+ };
43509
+ const frUiChatAttachments = {
43510
+ "ui.chat.attachments.pending_caption": "Pas encore envoyé",
43511
+ "ui.chat.attachments.file_meta": "{type} · {size}",
43512
+ "ui.chat.attachments.file_missing": "Déplacé ou supprimé",
43513
+ "ui.chat.attachments.file_retained": "Toujours conservé dans le stockage des pièces jointes ({digest})",
43514
+ "ui.chat.attachments.file_unnamed": "Pièce jointe",
43515
+ "ui.chat.attachments.copy_path": "Copier le chemin",
43516
+ "ui.chat.attachments.path_copied": "Chemin copié",
43517
+ "ui.chat.attachments.file_missing_recovery": "Le fichier n'est plus à cet emplacement.",
43518
+ "ui.chat.attachments.file_type_unknown": "Fichier",
43519
+ "ui.chat.attachments.copy_saved_path": "Copier le chemin de la copie conservée",
43520
+ "ui.chat.attachments.saved_path_copied": "Chemin de la copie conservée copié",
43521
+ "ui.chat.attachments.saved_copy_label": "Copie conservée"
43522
+ };
43114
43523
  const frUiChatComposer = {
43115
43524
  "ui.chat.composer.clear_message": "Effacer le message",
43116
43525
  "ui.chat.composer.message": "Message",
@@ -46752,6 +47161,34 @@ const hiUiCanvas = {
46752
47161
  "ui.canvas.files.prepareError": "इस कार्रवाई के लिए स्थानीय कॉपी तैयार नहीं हो सकी",
46753
47162
  "ui.canvas.files.pathCopyNote": "खोलने, फ़ोल्डर और पाथ की कार्रवाइयाँ इस बातचीत के स्क्रैचपैड में एक स्थानीय कॉपी बनाती हैं।"
46754
47163
  };
47164
+ const hiUiChatAsks = {
47165
+ "ui.chat.asks.context.messages_since": "{count, plural, one {इस सवाल के बाद से आपका # संदेश} other {इस सवाल के बाद से आपके # संदेश}} (अंतिम संदेश {time})",
47166
+ "ui.chat.asks.dismiss.answered_in_chat.note": "आप बातचीत में पहले ही जवाब दे चुके हैं",
47167
+ "ui.chat.asks.dismiss.answered_in_chat.title": "चैट में जवाब दिया गया",
47168
+ "ui.chat.asks.dismiss.cancel": "रद्द करें",
47169
+ "ui.chat.asks.dismiss.heading": "यह सवाल हटाएँ",
47170
+ "ui.chat.asks.dismiss.in_flight": "हटाया जा रहा है…",
47171
+ "ui.chat.asks.dismiss.moot.note": "एजेंट अब जवाब का इंतज़ार नहीं करेगा",
47172
+ "ui.chat.asks.dismiss.moot.title": "अब ज़रूरत नहीं",
47173
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "हटाया गया — चैट में तय हो गया",
47174
+ "ui.chat.asks.dismiss.receipt.moot": "हटाया गया — अब ज़रूरत नहीं",
47175
+ "ui.chat.asks.dismiss.refused": "यह सवाल हटाया नहीं जा सका — कुछ भी नहीं भेजा गया।",
47176
+ "ui.chat.asks.dismiss.trigger": "हटाएँ…"
47177
+ };
47178
+ const hiUiChatAttachments = {
47179
+ "ui.chat.attachments.pending_caption": "अभी तक नहीं भेजा गया",
47180
+ "ui.chat.attachments.file_meta": "{type} · {size}",
47181
+ "ui.chat.attachments.file_missing": "स्थानांतरित या हटाया गया",
47182
+ "ui.chat.attachments.file_retained": "अभी भी अनुलग्नक स्टोर में सुरक्षित है ({digest})",
47183
+ "ui.chat.attachments.file_unnamed": "अनुलग्नक फ़ाइल",
47184
+ "ui.chat.attachments.copy_path": "पाथ कॉपी करें",
47185
+ "ui.chat.attachments.path_copied": "पाथ कॉपी हो गया",
47186
+ "ui.chat.attachments.file_missing_recovery": "फ़ाइल अब इस पाथ पर नहीं है।",
47187
+ "ui.chat.attachments.file_type_unknown": "फ़ाइल",
47188
+ "ui.chat.attachments.copy_saved_path": "सहेजी गई प्रति का पाथ कॉपी करें",
47189
+ "ui.chat.attachments.saved_path_copied": "सहेजी गई प्रति का पाथ कॉपी हो गया",
47190
+ "ui.chat.attachments.saved_copy_label": "सहेजी गई प्रति"
47191
+ };
46755
47192
  const hiUiChatComposer = {
46756
47193
  "ui.chat.composer.clear_message": "संदेश मिटाएँ",
46757
47194
  "ui.chat.composer.message": "संदेश",
@@ -50351,6 +50788,34 @@ const ruUiCanvas = {
50351
50788
  "ui.canvas.files.videos": "Видео",
50352
50789
  "ui.canvas.files.working": "Рабочие файлы"
50353
50790
  };
50791
+ const ruUiChatAsks = {
50792
+ "ui.chat.asks.context.messages_since": "{count, plural, one {# сообщение от вас с момента этого запроса} few {# сообщения от вас с момента этого запроса} many {# сообщений от вас с момента этого запроса} other {# сообщения от вас с момента этого запроса}} (последнее сообщение: {time})",
50793
+ "ui.chat.asks.dismiss.answered_in_chat.note": "вы уже ответили в чате",
50794
+ "ui.chat.asks.dismiss.answered_in_chat.title": "Ответ дан в чате",
50795
+ "ui.chat.asks.dismiss.cancel": "Отмена",
50796
+ "ui.chat.asks.dismiss.heading": "Убрать этот запрос",
50797
+ "ui.chat.asks.dismiss.in_flight": "Убираем…",
50798
+ "ui.chat.asks.dismiss.moot.note": "агент больше не будет ждать ответа",
50799
+ "ui.chat.asks.dismiss.moot.title": "Больше не нужно",
50800
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "Убрано — решено в чате",
50801
+ "ui.chat.asks.dismiss.receipt.moot": "Убрано — больше не нужно",
50802
+ "ui.chat.asks.dismiss.refused": "Не удалось убрать этот запрос — ничего не отправлено.",
50803
+ "ui.chat.asks.dismiss.trigger": "Убрать…"
50804
+ };
50805
+ const ruUiChatAttachments = {
50806
+ "ui.chat.attachments.pending_caption": "Ещё не отправлено",
50807
+ "ui.chat.attachments.file_meta": "{type} · {size}",
50808
+ "ui.chat.attachments.file_missing": "Перемещено или удалено",
50809
+ "ui.chat.attachments.file_retained": "Всё ещё хранится в хранилище вложений ({digest})",
50810
+ "ui.chat.attachments.file_unnamed": "Вложенный файл",
50811
+ "ui.chat.attachments.copy_path": "Скопировать путь",
50812
+ "ui.chat.attachments.path_copied": "Путь скопирован",
50813
+ "ui.chat.attachments.file_missing_recovery": "Файла по этому пути больше нет.",
50814
+ "ui.chat.attachments.file_type_unknown": "Файл",
50815
+ "ui.chat.attachments.copy_saved_path": "Скопировать путь сохранённой копии",
50816
+ "ui.chat.attachments.saved_path_copied": "Путь сохранённой копии скопирован",
50817
+ "ui.chat.attachments.saved_copy_label": "Сохранённая копия"
50818
+ };
50354
50819
  const ruUiChatComposer = {
50355
50820
  "ui.chat.composer.ask_aside": "Спросить отдельно",
50356
50821
  "ui.chat.composer.attach_file": "Прикрепить файл",
@@ -53970,6 +54435,34 @@ const urUiCanvas = {
53970
54435
  "ui.canvas.files.prepareError": "اس عمل کے لیے مقامی نقل تیار نہیں ہو سکی",
53971
54436
  "ui.canvas.files.pathCopyNote": "کھولنے، فولڈر اور راستے کے اعمال اس گفتگو کے اسکریچ پیڈ میں مقامی نقل بناتے ہیں۔"
53972
54437
  };
54438
+ const urUiChatAsks = {
54439
+ "ui.chat.asks.context.messages_since": "{count, plural, one {اس سوال کے بعد سے آپ کا # پیغام} other {اس سوال کے بعد سے آپ کے # پیغامات}} (آخری پیغام {time})",
54440
+ "ui.chat.asks.dismiss.answered_in_chat.note": "آپ گفتگو میں پہلے ہی جواب دے چکے ہیں",
54441
+ "ui.chat.asks.dismiss.answered_in_chat.title": "چیٹ میں جواب دیا گیا",
54442
+ "ui.chat.asks.dismiss.cancel": "منسوخ کریں",
54443
+ "ui.chat.asks.dismiss.heading": "یہ سوال ہٹائیں",
54444
+ "ui.chat.asks.dismiss.in_flight": "ہٹایا جا رہا ہے…",
54445
+ "ui.chat.asks.dismiss.moot.note": "ایجنٹ اب جواب کا انتظار نہیں کرے گا",
54446
+ "ui.chat.asks.dismiss.moot.title": "اب ضرورت نہیں",
54447
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "ہٹا دیا — چیٹ میں طے ہو گیا",
54448
+ "ui.chat.asks.dismiss.receipt.moot": "ہٹا دیا — اب ضرورت نہیں",
54449
+ "ui.chat.asks.dismiss.refused": "یہ سوال ہٹایا نہیں جا سکا — کچھ نہیں بھیجا گیا۔",
54450
+ "ui.chat.asks.dismiss.trigger": "ہٹائیں…"
54451
+ };
54452
+ const urUiChatAttachments = {
54453
+ "ui.chat.attachments.pending_caption": "ابھی نہیں بھیجا گیا",
54454
+ "ui.chat.attachments.file_meta": "{type} · {size}",
54455
+ "ui.chat.attachments.file_missing": "منتقل یا حذف شدہ",
54456
+ "ui.chat.attachments.file_retained": "اب بھی منسلکات کے ذخیرے میں محفوظ ہے ({digest})",
54457
+ "ui.chat.attachments.file_unnamed": "منسلک فائل",
54458
+ "ui.chat.attachments.copy_path": "راستہ نقل کریں",
54459
+ "ui.chat.attachments.path_copied": "راستہ نقل ہو گیا",
54460
+ "ui.chat.attachments.file_missing_recovery": "فائل اب اس راستے پر موجود نہیں ہے۔",
54461
+ "ui.chat.attachments.file_type_unknown": "فائل",
54462
+ "ui.chat.attachments.copy_saved_path": "محفوظ شدہ نقل کا راستہ نقل کریں",
54463
+ "ui.chat.attachments.saved_path_copied": "محفوظ شدہ نقل کا راستہ نقل ہو گیا",
54464
+ "ui.chat.attachments.saved_copy_label": "محفوظ شدہ نقل"
54465
+ };
53973
54466
  const urUiChatComposer = {
53974
54467
  "ui.chat.composer.clear_message": "پیغام صاف کریں",
53975
54468
  "ui.chat.composer.message": "پیغام",
@@ -57589,6 +58082,34 @@ const viUiCanvas = {
57589
58082
  "ui.canvas.files.prepareError": "Không chuẩn bị được bản sao cục bộ cho thao tác này",
57590
58083
  "ui.canvas.files.pathCopyNote": "Các thao tác mở tệp, mở thư mục và thao tác với đường dẫn sẽ tạo bản sao cục bộ trong thư mục nháp của cuộc trò chuyện này."
57591
58084
  };
58085
+ const viUiChatAsks = {
58086
+ "ui.chat.asks.context.messages_since": "{count, plural, other {# tin nhắn của bạn kể từ khi câu hỏi này được đưa ra}} (tin nhắn gần nhất lúc {time})",
58087
+ "ui.chat.asks.dismiss.answered_in_chat.note": "bạn đã trả lời trong cuộc trò chuyện",
58088
+ "ui.chat.asks.dismiss.answered_in_chat.title": "Đã trả lời trong cuộc trò chuyện",
58089
+ "ui.chat.asks.dismiss.cancel": "Hủy",
58090
+ "ui.chat.asks.dismiss.heading": "Bỏ qua câu hỏi này",
58091
+ "ui.chat.asks.dismiss.in_flight": "Đang bỏ qua…",
58092
+ "ui.chat.asks.dismiss.moot.note": "tác nhân sẽ không chờ câu trả lời nữa",
58093
+ "ui.chat.asks.dismiss.moot.title": "Không còn cần nữa",
58094
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "Đã bỏ qua — đã giải quyết trong cuộc trò chuyện",
58095
+ "ui.chat.asks.dismiss.receipt.moot": "Đã bỏ qua — không còn cần nữa",
58096
+ "ui.chat.asks.dismiss.refused": "Không thể bỏ qua câu hỏi này — không có gì được gửi đi.",
58097
+ "ui.chat.asks.dismiss.trigger": "Bỏ qua…"
58098
+ };
58099
+ const viUiChatAttachments = {
58100
+ "ui.chat.attachments.pending_caption": "Chưa được gửi",
58101
+ "ui.chat.attachments.file_meta": "{type} · {size}",
58102
+ "ui.chat.attachments.file_missing": "Đã di chuyển hoặc bị xóa",
58103
+ "ui.chat.attachments.file_retained": "Vẫn được lưu trong kho tệp đính kèm ({digest})",
58104
+ "ui.chat.attachments.file_unnamed": "Tệp đính kèm",
58105
+ "ui.chat.attachments.copy_path": "Sao chép đường dẫn",
58106
+ "ui.chat.attachments.path_copied": "Đã sao chép đường dẫn",
58107
+ "ui.chat.attachments.file_missing_recovery": "Tệp không còn ở đường dẫn này.",
58108
+ "ui.chat.attachments.file_type_unknown": "Tệp",
58109
+ "ui.chat.attachments.copy_saved_path": "Sao chép đường dẫn bản sao đã lưu",
58110
+ "ui.chat.attachments.saved_path_copied": "Đã sao chép đường dẫn bản sao đã lưu",
58111
+ "ui.chat.attachments.saved_copy_label": "Bản sao đã lưu"
58112
+ };
57592
58113
  const viUiChatComposer = {
57593
58114
  "ui.chat.composer.clear_message": "Xóa tin nhắn",
57594
58115
  "ui.chat.composer.message": "Tin nhắn",
@@ -61017,6 +61538,34 @@ const zhCNUiCanvas = {
61017
61538
  "ui.canvas.files.prepareError": "无法为此操作准备本地副本",
61018
61539
  "ui.canvas.files.pathCopyNote": "打开文件、打开文件夹及路径相关操作会在此对话的暂存区中创建本地副本。"
61019
61540
  };
61541
+ const zhCNUiChatAsks = {
61542
+ "ui.chat.asks.context.messages_since": "{count, plural, other {自提出这个请求以来你发来了#条消息}}(最后一条消息于{time})",
61543
+ "ui.chat.asks.dismiss.answered_in_chat.note": "你已在对话中回复过",
61544
+ "ui.chat.asks.dismiss.answered_in_chat.title": "已在对话中回答",
61545
+ "ui.chat.asks.dismiss.cancel": "取消",
61546
+ "ui.chat.asks.dismiss.heading": "关闭此请求",
61547
+ "ui.chat.asks.dismiss.in_flight": "正在关闭……",
61548
+ "ui.chat.asks.dismiss.moot.note": "无需继续等待",
61549
+ "ui.chat.asks.dismiss.moot.title": "不再需要",
61550
+ "ui.chat.asks.dismiss.receipt.answered_in_chat": "已关闭——已在对话中解决",
61551
+ "ui.chat.asks.dismiss.receipt.moot": "已关闭——不再需要",
61552
+ "ui.chat.asks.dismiss.refused": "无法关闭此请求——未发送任何内容。",
61553
+ "ui.chat.asks.dismiss.trigger": "关闭……"
61554
+ };
61555
+ const zhCNUiChatAttachments = {
61556
+ "ui.chat.attachments.pending_caption": "尚未发送",
61557
+ "ui.chat.attachments.file_meta": "{type} · {size}",
61558
+ "ui.chat.attachments.file_missing": "已移动或删除",
61559
+ "ui.chat.attachments.file_retained": "仍保留在附件存储中({digest})",
61560
+ "ui.chat.attachments.file_unnamed": "附件",
61561
+ "ui.chat.attachments.copy_path": "复制路径",
61562
+ "ui.chat.attachments.path_copied": "路径已复制",
61563
+ "ui.chat.attachments.file_missing_recovery": "该文件已不在这个路径。",
61564
+ "ui.chat.attachments.file_type_unknown": "文件",
61565
+ "ui.chat.attachments.copy_saved_path": "复制已保存副本的路径",
61566
+ "ui.chat.attachments.saved_path_copied": "已复制保存副本的路径",
61567
+ "ui.chat.attachments.saved_copy_label": "保存的副本"
61568
+ };
61020
61569
  const zhCNUiChatComposer = {
61021
61570
  "ui.chat.composer.clear_message": "清空消息",
61022
61571
  "ui.chat.composer.message": "消息",
@@ -64047,6 +64596,8 @@ const bundledEnglish = {
64047
64596
  ...enUiBrowser,
64048
64597
  ...enUiCanvas,
64049
64598
  ...enUiChatAnalytics,
64599
+ ...enUiChatAsks,
64600
+ ...enUiChatAttachments,
64050
64601
  ...enUiChatCanonical,
64051
64602
  ...enUiChatCommentaryActions,
64052
64603
  ...enUiChatComposer,
@@ -64121,6 +64672,8 @@ const bundledCatalogues = {
64121
64672
  ...esUiBrowserChrome,
64122
64673
  ...esUiBrowser,
64123
64674
  ...esUiCanvas,
64675
+ ...esUiChatAsks,
64676
+ ...esUiChatAttachments,
64124
64677
  ...esUiChatComposer,
64125
64678
  ...esUiChatComposerMedia,
64126
64679
  ...esUiChatDialogs,
@@ -64185,6 +64738,8 @@ const bundledCatalogues = {
64185
64738
  ...frUiBrowserChrome,
64186
64739
  ...frUiBrowser,
64187
64740
  ...frUiCanvas,
64741
+ ...frUiChatAsks,
64742
+ ...frUiChatAttachments,
64188
64743
  ...frUiChatComposer,
64189
64744
  ...frUiChatComposerMedia,
64190
64745
  ...frUiChatDialogs,
@@ -64249,6 +64804,8 @@ const bundledCatalogues = {
64249
64804
  ...hiUiBrowserChrome,
64250
64805
  ...hiUiBrowser,
64251
64806
  ...hiUiCanvas,
64807
+ ...hiUiChatAsks,
64808
+ ...hiUiChatAttachments,
64252
64809
  ...hiUiChatComposer,
64253
64810
  ...hiUiChatComposerMedia,
64254
64811
  ...hiUiChatDialogs,
@@ -64310,6 +64867,8 @@ const bundledCatalogues = {
64310
64867
  ...ruUiBrowserChrome,
64311
64868
  ...ruUiBrowser,
64312
64869
  ...ruUiCanvas,
64870
+ ...ruUiChatAsks,
64871
+ ...ruUiChatAttachments,
64313
64872
  ...ruUiChatComposer,
64314
64873
  ...ruUiChatComposerMedia,
64315
64874
  ...ruUiChatDialogs,
@@ -64372,6 +64931,8 @@ const bundledCatalogues = {
64372
64931
  ...urUiBrowserChrome,
64373
64932
  ...urUiBrowser,
64374
64933
  ...urUiCanvas,
64934
+ ...urUiChatAsks,
64935
+ ...urUiChatAttachments,
64375
64936
  ...urUiChatComposer,
64376
64937
  ...urUiChatComposerMedia,
64377
64938
  ...urUiChatDialogs,
@@ -64434,6 +64995,8 @@ const bundledCatalogues = {
64434
64995
  ...viUiBrowserChrome,
64435
64996
  ...viUiBrowser,
64436
64997
  ...viUiCanvas,
64998
+ ...viUiChatAsks,
64999
+ ...viUiChatAttachments,
64437
65000
  ...viUiChatComposer,
64438
65001
  ...viUiChatComposerMedia,
64439
65002
  ...viUiChatDialogs,
@@ -64495,6 +65058,8 @@ const bundledCatalogues = {
64495
65058
  ...zhCNUiBrowserChrome,
64496
65059
  ...zhCNUiBrowser,
64497
65060
  ...zhCNUiCanvas,
65061
+ ...zhCNUiChatAsks,
65062
+ ...zhCNUiChatAttachments,
64498
65063
  ...zhCNUiChatComposer,
64499
65064
  ...zhCNUiChatComposerMedia,
64500
65065
  ...zhCNUiChatDialogs,
@@ -64806,6 +65371,160 @@ function pageOf(view) {
64806
65371
  title: view.webContents.isDestroyed() ? "" : view.webContents.getTitle()
64807
65372
  };
64808
65373
  }
65374
+ const LABELLABLE = /* @__PURE__ */ new Set([
65375
+ "checkbox",
65376
+ "radio",
65377
+ "switch",
65378
+ "textbox",
65379
+ "searchbox",
65380
+ "combobox",
65381
+ "listbox",
65382
+ "spinbutton",
65383
+ "slider",
65384
+ "button"
65385
+ ]);
65386
+ const CLICKABLE = /* @__PURE__ */ new Set([
65387
+ "button",
65388
+ "link",
65389
+ "menuitem",
65390
+ "menuitemcheckbox",
65391
+ "menuitemradio",
65392
+ "tab",
65393
+ "option",
65394
+ "treeitem",
65395
+ "DisclosureTriangle",
65396
+ "checkbox",
65397
+ "radio",
65398
+ "switch"
65399
+ ]);
65400
+ function normalise(value) {
65401
+ return String(value ?? "").replace(/\s+/g, " ").trim();
65402
+ }
65403
+ function readValue(raw) {
65404
+ const text = raw.trim();
65405
+ if (!text) return null;
65406
+ const quote = text[0];
65407
+ if (quote === '"' || quote === "'") {
65408
+ let value = "";
65409
+ for (let index = 1; index < text.length; index += 1) {
65410
+ const char = text[index] ?? "";
65411
+ if (char === "\\" && index + 1 < text.length) {
65412
+ value += text[index + 1] ?? "";
65413
+ index += 1;
65414
+ continue;
65415
+ }
65416
+ if (char === quote) {
65417
+ return index === text.length - 1 ? value : null;
65418
+ }
65419
+ value += char;
65420
+ }
65421
+ return null;
65422
+ }
65423
+ if (/[\[\]]/.test(text) || text.startsWith("*")) return null;
65424
+ return text;
65425
+ }
65426
+ function parseLocator(value) {
65427
+ const text = value.trim();
65428
+ const match = /^(role|label|text)(\*?)=([\s\S]+)$/i.exec(text);
65429
+ if (!match) return null;
65430
+ const kind = (match[1] ?? "").toLowerCase();
65431
+ const prefixSubstring = match[2] === "*";
65432
+ const rest = (match[3] ?? "").trim();
65433
+ if (kind === "role") {
65434
+ const roleMatch = /^([A-Za-z][A-Za-z0-9_-]*)\s*(?:\[\s*name(\*?)=\s*([\s\S]+?)\s*\])?$/.exec(rest);
65435
+ if (!roleMatch) return null;
65436
+ const role = roleMatch[1] ?? "";
65437
+ const substring = roleMatch[2] === "*";
65438
+ const rawName = roleMatch[3];
65439
+ const name2 = rawName === void 0 ? "" : readValue(rawName);
65440
+ if (name2 === null) return null;
65441
+ return { kind: "role", role: role.toLowerCase(), name: name2, substring };
65442
+ }
65443
+ const name = readValue(rest);
65444
+ if (name === null) return null;
65445
+ return { kind: kind === "label" ? "label" : "text", name, substring: prefixSubstring };
65446
+ }
65447
+ function matches(haystack, needle, substring) {
65448
+ const left = normalise(haystack).toLowerCase();
65449
+ const right = normalise(needle).toLowerCase();
65450
+ if (!right) return false;
65451
+ return substring ? left.includes(right) : left === right;
65452
+ }
65453
+ const NEVER_TARGETED = /* @__PURE__ */ new Set(["StaticText", "InlineTextBox", "none", "generic"]);
65454
+ function candidateNodes(nodes) {
65455
+ return nodes.filter((node) => !node.ignored && node.backendDOMNodeId !== void 0);
65456
+ }
65457
+ function resolveLocator(nodes, locator) {
65458
+ const pool = candidateNodes(nodes);
65459
+ const hits = [];
65460
+ const consider = (role, name) => {
65461
+ if (locator.kind === "role") {
65462
+ if (role.toLowerCase() !== locator.role) return false;
65463
+ if (locator.name && !matches(name, locator.name, locator.substring)) return false;
65464
+ return true;
65465
+ }
65466
+ return matches(name, locator.name, locator.substring);
65467
+ };
65468
+ const collect = (accept2) => {
65469
+ for (const node of pool) {
65470
+ const role = String(node.role?.value ?? "");
65471
+ const name = normalise(node.name?.value);
65472
+ if (!accept2(role)) continue;
65473
+ if (!consider(role, name)) continue;
65474
+ hits.push({ backendNodeId: node.backendDOMNodeId, role, name, matched: 0 });
65475
+ }
65476
+ };
65477
+ if (locator.kind === "role") {
65478
+ collect(() => true);
65479
+ } else if (locator.kind === "label") {
65480
+ collect((role) => LABELLABLE.has(role));
65481
+ if (!hits.length) collect((role) => CLICKABLE.has(role));
65482
+ } else {
65483
+ collect((role) => CLICKABLE.has(role));
65484
+ if (!hits.length) collect(() => true);
65485
+ }
65486
+ const first = hits[0];
65487
+ if (!first) return null;
65488
+ return { ...first, matched: hits.length };
65489
+ }
65490
+ function locatorSuggestions(nodes, query2, limit = 5) {
65491
+ const wanted = normalise(query2).toLowerCase();
65492
+ const parsed = parseLocator(query2);
65493
+ const term = parsed ? parsed.name.toLowerCase() : wanted;
65494
+ const words = term.split(/[^a-z0-9]+/).filter((word) => word.length > 1);
65495
+ const wantedRole = parsed && parsed.kind === "role" ? parsed.role : "";
65496
+ const scored = [];
65497
+ for (const node of candidateNodes(nodes)) {
65498
+ const role = String(node.role?.value ?? "");
65499
+ const name = normalise(node.name?.value);
65500
+ if (!name) continue;
65501
+ if (NEVER_TARGETED.has(role)) continue;
65502
+ const lowerName = name.toLowerCase();
65503
+ let score = 0;
65504
+ if (term && lowerName === term) score += 100;
65505
+ else if (term && lowerName.includes(term)) score += 60;
65506
+ for (const word of words) if (lowerName.includes(word)) score += 8;
65507
+ if (wantedRole && role.toLowerCase() === wantedRole) score += 25;
65508
+ if (CLICKABLE.has(role)) score += 12;
65509
+ if (!score) continue;
65510
+ scored.push({ score, role, name });
65511
+ }
65512
+ scored.sort((left, right) => right.score - left.score || left.name.localeCompare(right.name));
65513
+ const seen = /* @__PURE__ */ new Set();
65514
+ const out = [];
65515
+ for (const entry of scored) {
65516
+ const key = `${entry.role}\0${entry.name}`;
65517
+ if (seen.has(key)) continue;
65518
+ seen.add(key);
65519
+ out.push({ role: entry.role, name: entry.name, locator: locatorFor(entry.role, entry.name) });
65520
+ if (out.length >= limit) break;
65521
+ }
65522
+ return out;
65523
+ }
65524
+ function locatorFor(role, name) {
65525
+ const quoted = name.includes('"') && !name.includes("'") ? `'${name.replace(/\\/g, "\\\\")}'` : JSON.stringify(name);
65526
+ return role && /^[A-Za-z][A-Za-z0-9_-]*$/.test(role) ? `role=${role}[name=${quoted}]` : `text=${quoted}`;
65527
+ }
64809
65528
  const FRAME_HOP = ">>>";
64810
65529
  const MAX_FRAME_DEPTH = 3;
64811
65530
  const MAX_FRAMES_SEARCHED = 20;
@@ -65056,7 +65775,7 @@ async function resolveHops(ctx, contents, hops) {
65056
65775
  return { scope, nodeId };
65057
65776
  }
65058
65777
  async function searchFrames(ctx, contents, selector) {
65059
- const matches = [];
65778
+ const matches2 = [];
65060
65779
  const walk = await listFrames(ctx, contents);
65061
65780
  for (const scope of walk.frames) {
65062
65781
  const nodeId = await queryIn(ctx, contents, scope, selector).catch(
@@ -65065,9 +65784,9 @@ async function searchFrames(ctx, contents, selector) {
65065
65784
  return 0;
65066
65785
  }
65067
65786
  );
65068
- if (nodeId) matches.push({ scope, nodeId });
65787
+ if (nodeId) matches2.push({ scope, nodeId });
65069
65788
  }
65070
- return { matches, walk };
65789
+ return { matches: matches2, walk };
65071
65790
  }
65072
65791
  async function listFrames(ctx, contents) {
65073
65792
  const walk = {
@@ -65202,24 +65921,46 @@ async function editableInFrame(ctx, contents, scope, target2) {
65202
65921
  }
65203
65922
  const LANDMARKS = /* @__PURE__ */ new Set(["banner", "main", "navigation", "complementary", "contentinfo", "form", "region"]);
65204
65923
  const INTERACTIVE = /* @__PURE__ */ new Set(["button", "link", "textbox", "checkbox", "radio", "combobox", "menuitem", "tab", "switch", "slider", "spinbutton"]);
65205
- function compactAX(nodes, epoch) {
65924
+ function stateSuffix(node) {
65925
+ const truthy = (value) => value === true || value === "true";
65926
+ const falsy = (value) => value === false || value === "false";
65927
+ const property = (name) => node.properties?.find((entry) => entry.name === name)?.value.value;
65928
+ const markers = [];
65929
+ if (falsy(property("expanded"))) markers.push("collapsed");
65930
+ const checked = property("checked");
65931
+ if (truthy(checked)) markers.push("checked");
65932
+ else if (checked === "mixed") markers.push("mixed");
65933
+ if (truthy(property("disabled"))) markers.push("disabled");
65934
+ return markers.length ? ` [${markers.join("] [")}]` : "";
65935
+ }
65936
+ function compactAX(nodes, epoch, options = {}) {
65206
65937
  const byId = new Map(nodes.map((node) => [node.nodeId, node]));
65207
65938
  const refs = {};
65208
65939
  const lines = [];
65209
65940
  let sequence = 0;
65941
+ const scopeId = options.rootBackendNodeId;
65942
+ let scopeAt = null;
65943
+ let framesInScope = 0;
65210
65944
  const visited = /* @__PURE__ */ new Set();
65211
65945
  function visit2(node, depth) {
65212
65946
  if (visited.has(node.nodeId)) return;
65213
65947
  visited.add(node.nodeId);
65948
+ const savedScope = scopeAt;
65949
+ if (scopeAt === null && scopeId !== void 0 && node.backendDOMNodeId === scopeId) {
65950
+ scopeAt = depth;
65951
+ }
65952
+ const inside2 = scopeId === void 0 || scopeAt !== null && depth >= scopeAt;
65214
65953
  if (node.ignored) {
65215
65954
  for (const child of node.childIds ?? []) {
65216
65955
  const found = byId.get(child);
65217
65956
  if (found) visit2(found, depth);
65218
65957
  }
65958
+ scopeAt = savedScope;
65219
65959
  return;
65220
65960
  }
65221
65961
  const role = String(node.role?.value ?? "");
65222
65962
  const name = String(node.name?.value ?? "").trim();
65963
+ if (inside2 && role === "Iframe") framesInScope += 1;
65223
65964
  const focusable = node.properties?.some((property) => property.name === "focusable" && property.value.value === true);
65224
65965
  const interesting = INTERACTIVE.has(role) || LANDMARKS.has(role) || focusable || Boolean(name && role !== "StaticText");
65225
65966
  if (interesting) {
@@ -65228,16 +65969,54 @@ function compactAX(nodes, epoch) {
65228
65969
  ref = `e${++sequence}`;
65229
65970
  refs[ref] = { backendNodeId: node.backendDOMNodeId, epoch };
65230
65971
  }
65231
- lines.push(`${" ".repeat(depth)}- ${role || "node"}${name ? ` ${JSON.stringify(name)}` : ""}${ref ? ` [${ref}]` : ""}`);
65972
+ if (inside2) {
65973
+ const shown = depth - (scopeAt ?? 0);
65974
+ lines.push(`${" ".repeat(shown)}- ${role || "node"}${name ? ` ${JSON.stringify(name)}` : ""}${ref ? ` [${ref}]` : ""}${stateSuffix(node)}`);
65975
+ }
65232
65976
  }
65233
65977
  for (const child of node.childIds ?? []) {
65234
65978
  const found = byId.get(child);
65235
65979
  if (found) visit2(found, interesting ? depth + 1 : depth);
65236
65980
  }
65981
+ scopeAt = savedScope;
65982
+ }
65983
+ const scopedRoot = scopeId === void 0 ? void 0 : nodes.find((node) => node.backendDOMNodeId === scopeId);
65984
+ if (scopeId !== void 0 && scopedRoot === void 0) {
65985
+ return { snapshot: "", refs, framesInScope: 0, totalLines: 0, scoped: false };
65237
65986
  }
65238
65987
  const childIds = new Set(nodes.flatMap((node) => node.childIds ?? []));
65239
65988
  for (const root of nodes.filter((node) => !childIds.has(node.nodeId))) visit2(root, 0);
65240
- return { snapshot: lines.join("\n"), refs };
65989
+ return {
65990
+ snapshot: lines.join("\n"),
65991
+ refs,
65992
+ // Unscoped means "the whole page", and every frame is then read by the
65993
+ // caller's own frame walk: the count exists only to explain a SCOPE.
65994
+ framesInScope: scopeId === void 0 ? 0 : framesInScope,
65995
+ totalLines: lines.length,
65996
+ scoped: scopeId !== void 0
65997
+ };
65998
+ }
65999
+ function windowSnapshot(text, options = {}) {
66000
+ const lines = text ? text.split("\n").filter((line) => line.length > 0) : [];
66001
+ const totalLines = lines.length;
66002
+ const search = String(options.search ?? "").trim();
66003
+ const matched = search ? lines.filter((line) => line.toLowerCase().includes(search.toLowerCase())) : lines;
66004
+ const matchedLines = matched.length;
66005
+ const requested = Number(options.start ?? 1);
66006
+ const start = Number.isFinite(requested) && requested >= 1 ? Math.floor(requested) : 1;
66007
+ const rawLimit = Number(options.limit ?? 0);
66008
+ const limit = Number.isFinite(rawLimit) && rawLimit > 0 ? Math.floor(rawLimit) : 0;
66009
+ const end = limit ? start - 1 + limit : matchedLines;
66010
+ const page = matched.slice(start - 1, end);
66011
+ return {
66012
+ text: page.join("\n"),
66013
+ totalLines,
66014
+ matchedLines,
66015
+ returnedLines: page.length,
66016
+ start,
66017
+ limit,
66018
+ search
66019
+ };
65241
66020
  }
65242
66021
  const PAGE_OVERLAP_PX = 80;
65243
66022
  function defaultScrollExpression() {
@@ -65317,6 +66096,8 @@ async function read(ctx, params) {
65317
66096
  async function snapshot(ctx, params) {
65318
66097
  const record = ctx.registry.requireSurface(params.tab);
65319
66098
  const contents = record.view.webContents;
66099
+ const scope = stringParam(params, "selector");
66100
+ const rootBackendNodeId = scope ? await backendNodeIdForSelector(ctx, record.view, scope) : void 0;
65320
66101
  await ctx.cdp.send(contents, "Accessibility.enable", {});
65321
66102
  let nodes = [];
65322
66103
  try {
@@ -65330,22 +66111,49 @@ async function snapshot(ctx, params) {
65330
66111
  await ctx.cdp.send(contents, "Accessibility.disable", {}).catch(() => {
65331
66112
  });
65332
66113
  }
65333
- const { snapshot: topRendered, refs: topRefs } = compactAX(
65334
- nodes,
65335
- record.epoch
65336
- );
66114
+ const {
66115
+ snapshot: topRendered,
66116
+ refs: topRefs,
66117
+ scoped,
66118
+ framesInScope
66119
+ } = compactAX(nodes, record.epoch, { rootBackendNodeId });
66120
+ if (rootBackendNodeId !== void 0 && !scoped) {
66121
+ throw new BridgeCommandError(
66122
+ "element_not_found",
66123
+ `selector ${scope} matched an element with no accessibility subtree (hidden, aria-hidden, or inside a collapsed section); expand it first, then snapshot again`
66124
+ );
66125
+ }
65337
66126
  const refs = { ...topRefs };
65338
66127
  let rendered = topRendered;
65339
- if (nodes.some(isFrameNode)) {
66128
+ if (!scope && nodes.some(isFrameNode)) {
65340
66129
  const blocks = await frameSnapshots(ctx, record, nodes, refs);
65341
66130
  if (blocks.length) rendered = [rendered, ...blocks].join("\n");
65342
66131
  }
65343
66132
  record.refs = refs;
65344
66133
  ctx.registry.touch(record);
66134
+ let note = "";
66135
+ if (scope && framesInScope > 0) {
66136
+ const frames = framesInScope;
66137
+ note = `
66138
+ - (a scoped snapshot does not read frames: this section contains ${frames} frame${frames === 1 ? "" : "s"}; snapshot without selector= for their contents)`;
66139
+ }
66140
+ const page = windowSnapshot(rendered, {
66141
+ search: stringParam(params, "search"),
66142
+ start: numberParam(params, "start") ?? 1,
66143
+ limit: numberParam(params, "limit") ?? 0
66144
+ });
65345
66145
  return {
65346
- snapshot: rendered,
66146
+ snapshot: page.text + note,
65347
66147
  refs: Object.keys(refs).length,
65348
66148
  epoch: record.epoch,
66149
+ scoped,
66150
+ frames_in_scope: framesInScope,
66151
+ total_lines: page.totalLines,
66152
+ matched_lines: page.matchedLines,
66153
+ returned_lines: page.returnedLines,
66154
+ start: page.start,
66155
+ limit: page.limit,
66156
+ search: page.search,
65349
66157
  ...pageOf(record.view)
65350
66158
  };
65351
66159
  }
@@ -65591,11 +66399,48 @@ async function nodeIdForSelector(ctx, view, selector) {
65591
66399
  if (!queried?.nodeId) {
65592
66400
  throw new BridgeCommandError(
65593
66401
  "element_not_found",
65594
- `selector ${selector} matched nothing`
66402
+ `selector ${selector} matched nothing`,
66403
+ { candidates: await missCandidates(ctx, view, selector) }
65595
66404
  );
65596
66405
  }
65597
66406
  return queried.nodeId;
65598
66407
  }
66408
+ async function readAccessibilityTree(ctx, contents) {
66409
+ await ctx.cdp.send(contents, "Accessibility.enable", {});
66410
+ try {
66411
+ const tree = await ctx.cdp.send(
66412
+ contents,
66413
+ "Accessibility.getFullAXTree",
66414
+ {}
66415
+ );
66416
+ return Array.isArray(tree?.nodes) ? tree.nodes : [];
66417
+ } finally {
66418
+ await ctx.cdp.send(contents, "Accessibility.disable", {}).catch(() => {
66419
+ });
66420
+ }
66421
+ }
66422
+ async function missCandidates(ctx, view, selector) {
66423
+ const nodes = await readAccessibilityTree(ctx, view.webContents).catch(
66424
+ () => []
66425
+ );
66426
+ return locatorSuggestions(nodes, selector);
66427
+ }
66428
+ async function backendNodeIdForSelector(ctx, view, selector) {
66429
+ const nodeId = await nodeIdForSelector(ctx, view, selector);
66430
+ const described = await ctx.cdp.send(
66431
+ view.webContents,
66432
+ "DOM.describeNode",
66433
+ { nodeId, depth: 0 }
66434
+ );
66435
+ const backendNodeId = described?.node?.backendNodeId;
66436
+ if (backendNodeId === void 0) {
66437
+ throw new BridgeCommandError(
66438
+ "element_not_found",
66439
+ `selector ${selector} has no backend node id`
66440
+ );
66441
+ }
66442
+ return backendNodeId;
66443
+ }
65599
66444
  const CLICK_FUNCTION = `function () {
65600
66445
  const r = this.getBoundingClientRect();
65601
66446
  const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
@@ -65694,6 +66539,16 @@ const READ_VALUE_FUNCTION = `function () {
65694
66539
  return String(this.textContent || '');
65695
66540
  }`;
65696
66541
  const SNAPSHOT_REF = /^e\d+$/;
66542
+ function resolvedFields(node) {
66543
+ return node.resolvedBy === "locator" ? {
66544
+ resolved: {
66545
+ by: "locator",
66546
+ role: node.role ?? "",
66547
+ name: node.name ?? "",
66548
+ matched: node.matched ?? 1
66549
+ }
66550
+ } : {};
66551
+ }
65697
66552
  function frameFields(node) {
65698
66553
  return node.frame ? { frame_origin: node.frame.origin ?? "" } : {};
65699
66554
  }
@@ -65721,6 +66576,39 @@ async function resolveNode(ctx, record, target2) {
65721
66576
  }
65722
66577
  return finishTop(ctx, contents, target2, pushedId, ref.frameOrigin);
65723
66578
  }
66579
+ const locator = parseLocator(target2);
66580
+ if (locator) {
66581
+ const nodes = await readAccessibilityTree(ctx, contents);
66582
+ const hit = resolveLocator(nodes, locator);
66583
+ if (!hit) {
66584
+ throw new BridgeCommandError(
66585
+ "element_not_found",
66586
+ `no element matches ${target2}`,
66587
+ { candidates: locatorSuggestions(nodes, target2), matched: 0 }
66588
+ );
66589
+ }
66590
+ await ctx.cdp.send(contents, "DOM.getDocument", { depth: 0 });
66591
+ const pushed = await ctx.cdp.send(
66592
+ contents,
66593
+ "DOM.pushNodesByBackendIdsToFrontend",
66594
+ { backendNodeIds: [hit.backendNodeId] }
66595
+ );
66596
+ const nodeId2 = pushed?.nodeIds?.[0];
66597
+ if (nodeId2 === void 0) {
66598
+ throw new BridgeCommandError(
66599
+ "element_not_found",
66600
+ `${target2} is no longer in the document`
66601
+ );
66602
+ }
66603
+ const resolved = await finishTop(ctx, contents, target2, nodeId2);
66604
+ return {
66605
+ ...resolved,
66606
+ resolvedBy: "locator",
66607
+ role: hit.role,
66608
+ name: hit.name,
66609
+ matched: hit.matched
66610
+ };
66611
+ }
65724
66612
  const hops = parseFrameHops(target2);
65725
66613
  if (hops) {
65726
66614
  const hit = await resolveHops(ctx, contents, hops);
@@ -65733,8 +66621,8 @@ async function resolveNode(ctx, record, target2) {
65733
66621
  if (!(error instanceof BridgeCommandError) || !TOP_MISS.test(error.message)) {
65734
66622
  throw error;
65735
66623
  }
65736
- const { matches, walk } = await searchFrames(ctx, contents, target2);
65737
- if (matches.length === 0) {
66624
+ const { matches: matches2, walk } = await searchFrames(ctx, contents, target2);
66625
+ if (matches2.length === 0) {
65738
66626
  if (!walk.truncated && walk.skipped === 0 && walk.denied === 0)
65739
66627
  throw error;
65740
66628
  throw new BridgeCommandError(
@@ -65742,21 +66630,21 @@ async function resolveNode(ctx, record, target2) {
65742
66630
  `selector ${target2} matched nothing in the page itself or the frames searched (${walkNote(walk)}); address the frame explicitly as <iframe-selector> ${FRAME_HOP} <selector>`
65743
66631
  );
65744
66632
  }
65745
- if (matches.length === 1 && (walk.truncated || walk.skipped > 0)) {
65746
- const candidate = candidatePath(matches[0].scope, target2);
66633
+ if (matches2.length === 1 && (walk.truncated || walk.skipped > 0)) {
66634
+ const candidate = candidatePath(matches2[0].scope, target2);
65747
66635
  throw new BridgeCommandError(
65748
66636
  "element_not_found",
65749
66637
  `selector ${target2} matched nothing in the page itself and one element in the frames searched, but not every frame was searched (${walkNote(walk)}), so nothing was done; if this is the one, name it: ${candidate}`,
65750
66638
  { candidates: [candidate] }
65751
66639
  );
65752
66640
  }
65753
- if (matches.length > 1) {
65754
- const candidates = matches.map(
66641
+ if (matches2.length > 1) {
66642
+ const candidates = matches2.map(
65755
66643
  (match) => candidatePath(match.scope, target2)
65756
66644
  );
65757
66645
  throw new BridgeCommandError(
65758
66646
  "element_not_found",
65759
- `selector ${target2} matched nothing in the page itself and ${matches.length} elements inside its frames, so nothing was done; name one: ${candidates.join(", ")}`,
66647
+ `selector ${target2} matched nothing in the page itself and ${matches2.length} elements inside its frames, so nothing was done; name one: ${candidates.join(", ")}`,
65760
66648
  { candidates }
65761
66649
  );
65762
66650
  }
@@ -65764,8 +66652,8 @@ async function resolveNode(ctx, record, target2) {
65764
66652
  ctx,
65765
66653
  contents,
65766
66654
  target2,
65767
- matches[0].scope,
65768
- matches[0].nodeId
66655
+ matches2[0].scope,
66656
+ matches2[0].nodeId
65769
66657
  );
65770
66658
  }
65771
66659
  return finishTop(ctx, contents, target2, nodeId);
@@ -65918,6 +66806,7 @@ async function click(ctx, params) {
65918
66806
  return {
65919
66807
  navigated: navigationSeen || after.url !== before,
65920
66808
  ...frameFields(node),
66809
+ ...resolvedFields(node),
65921
66810
  ...after
65922
66811
  };
65923
66812
  }
@@ -65930,7 +66819,12 @@ async function type(ctx, params) {
65930
66819
  const typed = await typeInto(ctx, contents, node, text, target2);
65931
66820
  if (typed) {
65932
66821
  ctx.registry.touch(record);
65933
- return { ...typed, ...frameFields(node), ...pageOf(record.view) };
66822
+ return {
66823
+ ...typed,
66824
+ ...frameFields(node),
66825
+ ...resolvedFields(node),
66826
+ ...pageOf(record.view)
66827
+ };
65934
66828
  }
65935
66829
  const frame = await iframeScopeOf(ctx, contents, node);
65936
66830
  if (frame) {
@@ -66091,6 +66985,7 @@ function clampTimeout(requested) {
66091
66985
  const READ_STYLES_SOURCE = '(selector, properties) => {\n const DEFAULTS = [\n "display",\n "position",\n "visibility",\n "opacity",\n "z-index",\n "transform",\n "transform-origin",\n "top",\n "right",\n "bottom",\n "left",\n "width",\n "height",\n "contain",\n "filter",\n "will-change",\n "overflow",\n "overflow-x",\n "overflow-y",\n "pointer-events"\n ];\n const MAX_MATCHES = 5;\n const MAX_STYLES = 30;\n const MAX_INLINE = 30;\n const MAX_IDENTITY = 120;\n const MAX_VALUE = 200;\n const all = document.querySelectorAll(selector);\n if (!all || all.length === 0) return null;\n const names = DEFAULTS.slice();\n if (Array.isArray(properties)) {\n for (let i = 0; i < properties.length && names.length < MAX_STYLES; i++) {\n const name = String(properties[i] ?? "").trim();\n if (name && names.indexOf(name) === -1) names.push(name);\n }\n }\n const round2 = (value) => {\n const rounded = Math.round(value * 100) / 100;\n return rounded === 0 ? 0 : rounded;\n };\n const clip = (value, limit) => value.length > limit ? value.slice(0, limit - 1) + "\\u2026" : value;\n const rectOf = (el) => {\n const r = el.getBoundingClientRect();\n return {\n x: round2(r.x),\n y: round2(r.y),\n top: round2(r.top),\n right: round2(r.right),\n bottom: round2(r.bottom),\n width: round2(r.width),\n height: round2(r.height)\n };\n };\n const attrOf = (el, name) => clip(el.getAttribute(name) || "", MAX_IDENTITY);\n const matches = [];\n const first = Array.from(all).slice(0, MAX_MATCHES);\n for (const el of first) {\n const computed = getComputedStyle(el);\n const styles = {};\n for (const name of names) {\n styles[name] = clip(String(computed.getPropertyValue(name) || "").trim(), MAX_VALUE);\n }\n const inline = {};\n const declaration = el.style;\n let inlineCount = 0;\n for (let j = 0; j < declaration.length && inlineCount < MAX_INLINE; j++) {\n const name = declaration.item(j);\n if (name && name.indexOf("--") === 0) {\n const key = clip(name, MAX_IDENTITY);\n if (!(key in inline)) {\n inline[key] = clip(\n String(declaration.getPropertyValue(name) || "").trim(),\n MAX_VALUE\n );\n inlineCount += 1;\n }\n }\n }\n matches.push({\n tag: clip(el.tagName.toLowerCase(), MAX_IDENTITY),\n id: attrOf(el, "id"),\n role: attrOf(el, "role"),\n className: attrOf(el, "class"),\n rect: rectOf(el),\n styles,\n inline\n });\n }\n return { count: matches.length, matches, truncated: all.length > matches.length };\n}';
66092
66986
  const HIT_TEST_SOURCE = '(x, y) => {\n const PROPS = ["display", "position", "visibility", "opacity", "z-index", "pointer-events"];\n const MAX_ELEMENTS = 8;\n const MAX_IDENTITY = 120;\n const MAX_VALUE = 200;\n const px = Number(x);\n const py = Number(y);\n if (!Number.isFinite(px) || !Number.isFinite(py)) return null;\n const stack = document.elementsFromPoint(px, py);\n if (!stack || stack.length === 0) return null;\n const round2 = (value) => {\n const rounded = Math.round(value * 100) / 100;\n return rounded === 0 ? 0 : rounded;\n };\n const clip = (value, limit) => value.length > limit ? value.slice(0, limit - 1) + "\\u2026" : value;\n const attrOf = (el, name) => clip(el.getAttribute(name) || "", MAX_IDENTITY);\n const elements = [];\n for (const el of stack.slice(0, MAX_ELEMENTS)) {\n const computed = getComputedStyle(el);\n const styles = {};\n for (const name of PROPS) {\n styles[name] = clip(String(computed.getPropertyValue(name) || "").trim(), MAX_VALUE);\n }\n const r = el.getBoundingClientRect();\n elements.push({\n tag: clip(el.tagName.toLowerCase(), MAX_IDENTITY),\n id: attrOf(el, "id"),\n role: attrOf(el, "role"),\n className: attrOf(el, "class"),\n rect: {\n x: round2(r.x),\n y: round2(r.y),\n top: round2(r.top),\n right: round2(r.right),\n bottom: round2(r.bottom),\n width: round2(r.width),\n height: round2(r.height)\n },\n styles\n });\n }\n return { count: elements.length, elements };\n}';
66093
66987
  const ANCESTORS_SOURCE = '(selector, depth) => {\n const PROPS = [\n "display",\n "position",\n "visibility",\n "overflow",\n "overflow-x",\n "overflow-y",\n "transform",\n "transform-origin",\n "contain",\n "filter",\n "will-change",\n "z-index",\n "isolation",\n "top",\n "left"\n ];\n const DEFAULT_DEPTH = 12;\n const MAX_DEPTH = 16;\n const MAX_IDENTITY = 120;\n const MAX_VALUE = 200;\n const el = document.querySelector(selector);\n if (!el) return null;\n const wanted = typeof depth === "number" && Number.isFinite(depth) ? Math.floor(depth) : DEFAULT_DEPTH;\n const bound = Math.max(1, Math.min(MAX_DEPTH, wanted));\n const round2 = (value) => {\n const rounded = Math.round(value * 100) / 100;\n return rounded === 0 ? 0 : rounded;\n };\n const clip = (value, limit) => value.length > limit ? value.slice(0, limit - 1) + "\\u2026" : value;\n const attrOf = (el2, name) => clip(el2.getAttribute(name) || "", MAX_IDENTITY);\n const chain = [];\n let node = el;\n while (node && chain.length < bound) {\n const computed = getComputedStyle(node);\n const styles = {};\n for (const name of PROPS) {\n styles[name] = clip(String(computed.getPropertyValue(name) || "").trim(), MAX_VALUE);\n }\n const r = node.getBoundingClientRect();\n chain.push({\n tag: clip(node.tagName.toLowerCase(), MAX_IDENTITY),\n id: attrOf(node, "id"),\n role: attrOf(node, "role"),\n className: attrOf(node, "class"),\n rect: {\n x: round2(r.x),\n y: round2(r.y),\n top: round2(r.top),\n right: round2(r.right),\n bottom: round2(r.bottom),\n width: round2(r.width),\n height: round2(r.height)\n },\n styles\n });\n if (node === document.documentElement) break;\n node = node.parentElement;\n }\n return { count: chain.length, chain };\n}';
66988
+ const QUERY_SOURCE = '(selector, limit) => {\n const MAX_ELEMENTS = 50;\n const MAX_IDENTITY = 120;\n const MAX_TEXT = 120;\n const MAX_VALUE = 120;\n if (typeof selector !== "string" || !selector.trim()) return null;\n const wanted = typeof limit === "number" && Number.isFinite(limit) ? Math.floor(limit) : 20;\n const bound = Math.max(1, Math.min(MAX_ELEMENTS, wanted));\n const round2 = (value) => {\n const rounded = Math.round(value * 100) / 100;\n return rounded === 0 ? 0 : rounded;\n };\n const clip = (value, cap) => value.length > cap ? `${value.slice(0, cap - 1)}\\u2026` : value;\n const attrOf = (el, name) => clip(String(el.getAttribute(name) || ""), MAX_IDENTITY);\n const visibleOf = (el, rect) => {\n if (rect.width <= 0 || rect.height <= 0) return false;\n const computed = getComputedStyle(el);\n if (computed.display === "none" || computed.visibility === "hidden") return false;\n return computed.contentVisibility !== "hidden";\n };\n let nodes;\n try {\n nodes = Array.from(document.querySelectorAll(selector));\n } catch {\n return { count: 0, truncated: false, matches: [], error: "invalid_selector" };\n }\n const viewport = {\n width: document.documentElement.clientWidth || window.innerWidth || 0,\n height: document.documentElement.clientHeight || window.innerHeight || 0\n };\n const matches = [];\n for (const el of nodes.slice(0, bound)) {\n const rect = el.getBoundingClientRect();\n const tag = clip(String(el.tagName || "").toLowerCase(), MAX_IDENTITY);\n const type = attrOf(el, "type").toLowerCase();\n const isPassword = tag === "input" && type === "password";\n const rawValue = el.value;\n const checkable = tag === "input" && (type === "checkbox" || type === "radio");\n const checked = checkable ? Boolean(el.checked) : null;\n matches.push({\n tag,\n id: attrOf(el, "id"),\n name: attrOf(el, "name"),\n role: attrOf(el, "role"),\n ariaLabel: attrOf(el, "aria-label"),\n // `innerText` is the RENDERED text (a hidden node reports ""), which is\n // what an agent reading a page means by "the text"; `textContent` would\n // report text nobody can see, including inside a collapsed section.\n text: clip(String(el.innerText || "").replace(/\\s+/g, " ").trim(), MAX_TEXT),\n type,\n visible: visibleOf(el, rect),\n inView: rect.top < viewport.height && rect.bottom > 0 && rect.left < viewport.width && rect.right > 0,\n disabled: el.hasAttribute("disabled") || el.disabled === true,\n checked,\n value: isPassword || rawValue === void 0 || rawValue === null ? "" : clip(String(rawValue), MAX_VALUE),\n rect: {\n x: round2(rect.x),\n y: round2(rect.y),\n width: round2(rect.width),\n height: round2(rect.height)\n }\n });\n }\n return { count: nodes.length, truncated: nodes.length > matches.length, matches };\n}';
66094
66989
  const MAX_REQUESTED_PROPERTIES = 20;
66095
66990
  const DEFAULT_DEPTH = 12;
66096
66991
  const MAX_DEPTH = 16;
@@ -66217,6 +67112,51 @@ async function ancestors(ctx, params) {
66217
67112
  ...pageOf(record.view)
66218
67113
  };
66219
67114
  }
67115
+ const DEFAULT_LIMIT = 20;
67116
+ const MAX_LIMIT = 50;
67117
+ async function query(ctx, params) {
67118
+ const record = ctx.registry.requireSurface(params.tab);
67119
+ const selector = stringParam(params, "selector");
67120
+ if (!selector) {
67121
+ throw new BridgeCommandError("element_not_found", "selector is required");
67122
+ }
67123
+ const requested = numberParam(params, "limit") ?? DEFAULT_LIMIT;
67124
+ const limit = Math.max(1, Math.min(MAX_LIMIT, Math.floor(requested)));
67125
+ const code = `(${QUERY_SOURCE})(${JSON.stringify(selector)}, ${JSON.stringify(limit)})`;
67126
+ let result;
67127
+ try {
67128
+ result = await deadline(
67129
+ record.view.webContents.executeJavaScriptInIsolatedWorld(
67130
+ ISOLATED_WORLD_ID,
67131
+ [{ code }]
67132
+ ),
67133
+ SCRIPTING_DEADLINE_MS,
67134
+ `query(${record.tabId})`
67135
+ );
67136
+ } catch (error) {
67137
+ if (error instanceof Error && INVALID_SELECTOR.test(error.message)) {
67138
+ throw new BridgeCommandError(
67139
+ "element_not_found",
67140
+ `selector ${selector} is not valid`
67141
+ );
67142
+ }
67143
+ throw error;
67144
+ }
67145
+ if (typeof result !== "object" || result === null) {
67146
+ throw new BridgeCommandError(
67147
+ "internal",
67148
+ "the page returned no structured query result"
67149
+ );
67150
+ }
67151
+ if (result.error === "invalid_selector") {
67152
+ throw new BridgeCommandError(
67153
+ "element_not_found",
67154
+ `selector ${selector} is not valid`
67155
+ );
67156
+ }
67157
+ ctx.registry.touch(record);
67158
+ return { ...result, ...pageOf(record.view) };
67159
+ }
66220
67160
  const MAX_AGENT_TABS = 8;
66221
67161
  const BACKGROUND_VIEWPORT = {
66222
67162
  x: 0,
@@ -67104,7 +68044,7 @@ async function upload(ctx, params) {
67104
68044
  { selector, frame_origin: node.frame.origin ?? "" }
67105
68045
  );
67106
68046
  }
67107
- const accept = await acceptsOf(ctx, record, node.nodeId);
68047
+ const accept2 = await acceptsOf(ctx, record, node.nodeId);
67108
68048
  await ctx.cdp.send(contents, "DOM.setFileInputFiles", {
67109
68049
  files: paths,
67110
68050
  nodeId: node.nodeId
@@ -67112,7 +68052,7 @@ async function upload(ctx, params) {
67112
68052
  if (!/not a file input/i.test(String(error))) throw error;
67113
68053
  throw new BridgeCommandError("element_not_found", NOT_A_FILE_INPUT, {
67114
68054
  selector,
67115
- accept
68055
+ accept: accept2
67116
68056
  });
67117
68057
  });
67118
68058
  let held = null;
@@ -67126,10 +68066,10 @@ async function upload(ctx, params) {
67126
68066
  if (!held) {
67127
68067
  throw new BridgeCommandError("element_not_found", NOT_A_FILE_INPUT, {
67128
68068
  selector,
67129
- accept
68069
+ accept: accept2
67130
68070
  });
67131
68071
  }
67132
- assertHolds(paths, held, selector, accept);
68072
+ assertHolds(paths, held, selector, accept2);
67133
68073
  }
67134
68074
  ctx.registry.touch(record);
67135
68075
  const facts = paths.map(factOf);
@@ -67210,7 +68150,7 @@ async function readBack(ctx, record, objectId) {
67210
68150
  });
67211
68151
  return answer?.result?.value ?? null;
67212
68152
  }
67213
- function assertHolds(paths, held, selector, accept) {
68153
+ function assertHolds(paths, held, selector, accept2) {
67214
68154
  const expected = paths.map((path2) => path$1.basename(path2));
67215
68155
  const mismatch = held.count !== expected.length || expected.some((name, index) => held.names[index] !== name) || paths.some((path2, index) => sizeOf(path2) !== held.sizes[index]);
67216
68156
  if (!mismatch) return;
@@ -67221,7 +68161,7 @@ function assertHolds(paths, held, selector, accept) {
67221
68161
  selector,
67222
68162
  attached: expected,
67223
68163
  held: held.names,
67224
- accept,
68164
+ accept: accept2,
67225
68165
  reason: "read_back_mismatch"
67226
68166
  }
67227
68167
  );
@@ -67376,7 +68316,7 @@ function collectAddressHistory(tabs2, activeTabId) {
67376
68316
  function foldAscii(value) {
67377
68317
  return value.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
67378
68318
  }
67379
- function mergeAddressHistory(tabEntries, historyEntries, query, limit) {
68319
+ function mergeAddressHistory(tabEntries, historyEntries, query2, limit) {
67380
68320
  const cap = Math.max(0, Math.floor(limit));
67381
68321
  if (cap === 0) return [];
67382
68322
  const seen = /* @__PURE__ */ new Set();
@@ -67394,7 +68334,7 @@ function mergeAddressHistory(tabEntries, historyEntries, query, limit) {
67394
68334
  source: "history"
67395
68335
  });
67396
68336
  }
67397
- const needle = foldAscii(query.trim());
68337
+ const needle = foldAscii(query2.trim());
67398
68338
  for (const entry of tabEntries) {
67399
68339
  if (merged.length >= cap) break;
67400
68340
  if (seen.has(entry.url)) continue;
@@ -67828,25 +68768,25 @@ class PageControls {
67828
68768
  };
67829
68769
  active.view.webContents.on("found-in-page", this.listener);
67830
68770
  }
67831
- search(active, query, forward, next) {
68771
+ search(active, query2, forward, next) {
67832
68772
  this.open(active);
67833
68773
  if (!this.find || this.record !== active) return;
67834
68774
  this.stopTimer();
67835
- const same = this.find.query === query;
68775
+ const same = this.find.query === query2;
67836
68776
  const continuing = same && next && this.requestId !== null && !this.find.unreported;
67837
68777
  this.requestId = null;
67838
68778
  this.find = {
67839
68779
  ...this.find,
67840
- query,
67841
- pending: query.length > 0,
68780
+ query: query2,
68781
+ pending: query2.length > 0,
67842
68782
  unreported: false,
67843
68783
  ...same ? {} : { ordinal: 0, matches: 0 }
67844
68784
  };
67845
- if (!query) {
68785
+ if (!query2) {
67846
68786
  active.view.webContents.stopFindInPage?.("clearSelection");
67847
68787
  return;
67848
68788
  }
67849
- this.requestId = active.view.webContents.findInPage?.(query, {
68789
+ this.requestId = active.view.webContents.findInPage?.(query2, {
67850
68790
  forward,
67851
68791
  // Electron's misleading name means NEW SESSION (44.3 native maps
67852
68792
  // it to options->new_session), not "advance". Initial/changed/retry
@@ -68001,6 +68941,12 @@ const DOCUMENT_SCOPED = /* @__PURE__ */ new Set([
68001
68941
  "hit_test",
68002
68942
  "ancestors",
68003
68943
  "snapshot",
68944
+ // `query` is a read of the CURRENT document's DOM (CSS in, descriptors out),
68945
+ // so it is authorized against that document on entry and again on the result
68946
+ // exactly as `read` is: an answer that raced a navigation it did not cause
68947
+ // must be discarded, not reported as the new page's (agent review round 2 on
68948
+ // the query slice — the action was missing from BOTH sets).
68949
+ "query",
68004
68950
  "screenshot",
68005
68951
  "click",
68006
68952
  "type",
@@ -68031,6 +68977,7 @@ const TAB_SCOPED = /* @__PURE__ */ new Set([
68031
68977
  "hit_test",
68032
68978
  "ancestors",
68033
68979
  "snapshot",
68980
+ "query",
68034
68981
  "screenshot",
68035
68982
  "click",
68036
68983
  "type",
@@ -68248,6 +69195,8 @@ class BrowserHost {
68248
69195
  return hitTest(this, params);
68249
69196
  case "ancestors":
68250
69197
  return ancestors(this, params);
69198
+ case "query":
69199
+ return query(this, params);
68251
69200
  case "snapshot":
68252
69201
  return snapshot(this, params);
68253
69202
  case "scroll":
@@ -68659,7 +69608,7 @@ class BrowserHost {
68659
69608
  onChordFocusChanged;
68660
69609
  /** An explicit tab id fences queued chrome intents: a switch between key
68661
69610
  * delivery and IPC must never search/zoom the newly active tab instead. */
68662
- async controlPage(tabId, action, query = "", forward = true, next = false) {
69611
+ async controlPage(tabId, action, query2 = "", forward = true, next = false) {
68663
69612
  return this.registry.lane(tabId, async () => {
68664
69613
  const active = this.registry.activeTab;
68665
69614
  if (!active || active.tabId !== tabId || !active.presented || active.view.webContents.isDestroyed())
@@ -68668,7 +69617,7 @@ class BrowserHost {
68668
69617
  else if (action === "print") this.pageControls.print(active);
68669
69618
  else if (action === "close") this.pageControls.clear();
68670
69619
  else if (action === "search")
68671
- this.pageControls.search(active, query, forward, next);
69620
+ this.pageControls.search(active, query2, forward, next);
68672
69621
  else this.pageControls.zoom(active, action);
68673
69622
  this.onChanged();
68674
69623
  return this.chromeState();
@@ -70318,18 +71267,18 @@ function registerBrowserIpc(options) {
70318
71267
  });
70319
71268
  electron.ipcMain.handle(
70320
71269
  "browser-page-control",
70321
- (event, tabId, action, query, forward, next) => {
71270
+ (event, tabId, action, query2, forward, next) => {
70322
71271
  const host = authorize(event);
70323
71272
  if (typeof action !== "string" || !["open", "close", "search", "print", "in", "out", "reset"].includes(
70324
71273
  action
70325
71274
  ))
70326
71275
  throw new Error("Invalid page control action.");
70327
- if (typeof query !== "string" || query.length > 4096 || typeof forward !== "boolean" || typeof next !== "boolean")
71276
+ if (typeof query2 !== "string" || query2.length > 4096 || typeof forward !== "boolean" || typeof next !== "boolean")
70328
71277
  throw new Error("Invalid page search input.");
70329
71278
  return host.controlPage(
70330
71279
  numberOrThrow(tabId, "tabId"),
70331
71280
  action,
70332
- query,
71281
+ query2,
70333
71282
  forward,
70334
71283
  next
70335
71284
  );
@@ -70539,14 +71488,14 @@ function addressHistoryRequestOrThrow(value) {
70539
71488
  if (typeof request.query !== "string") {
70540
71489
  throw new Error("The address history query must be a string.");
70541
71490
  }
70542
- const query = request.query;
70543
- if (query.length > ADDRESS_HISTORY_MAX_QUERY_LENGTH || CONTROL_CHARS.test(query)) {
71491
+ const query2 = request.query;
71492
+ if (query2.length > ADDRESS_HISTORY_MAX_QUERY_LENGTH || CONTROL_CHARS.test(query2)) {
70544
71493
  throw new Error("The address history query is invalid.");
70545
71494
  }
70546
71495
  if (typeof request.limit !== "number" || !Number.isSafeInteger(request.limit) || request.limit < 1 || request.limit > ADDRESS_HISTORY_MAX_LIMIT) {
70547
71496
  throw new Error("The address history limit is invalid.");
70548
71497
  }
70549
- return { requestId: requestId2, expectedTabId, query, limit: request.limit };
71498
+ return { requestId: requestId2, expectedTabId, query: query2, limit: request.limit };
70550
71499
  }
70551
71500
  function closeTabsIntentOrThrow(value) {
70552
71501
  if (!value || typeof value !== "object") {
@@ -74380,6 +75329,41 @@ class DesktopNotifier {
74380
75329
  notification.show();
74381
75330
  }
74382
75331
  }
75332
+ const ATTACH_FLAG = "--attach";
75333
+ function readAttachArguments(argv) {
75334
+ const paths = [];
75335
+ const problems = [];
75336
+ for (let index = 0; index < argv.length; index += 1) {
75337
+ const argument = argv[index];
75338
+ if (argument === ATTACH_FLAG) {
75339
+ const value = argv[index + 1];
75340
+ if (!value || value.startsWith("--")) {
75341
+ problems.push(`${ATTACH_FLAG} needs an absolute path`);
75342
+ continue;
75343
+ }
75344
+ index += 1;
75345
+ accept(value, paths, problems);
75346
+ continue;
75347
+ }
75348
+ if (argument.startsWith(`${ATTACH_FLAG}=`)) {
75349
+ accept(argument.slice(ATTACH_FLAG.length + 1), paths, problems);
75350
+ }
75351
+ }
75352
+ return { paths, problems };
75353
+ }
75354
+ function accept(value, paths, problems) {
75355
+ if (!value.trim()) {
75356
+ problems.push(`${ATTACH_FLAG} needs an absolute path`);
75357
+ return;
75358
+ }
75359
+ if (!path$1.isAbsolute(value)) {
75360
+ problems.push(
75361
+ `${ATTACH_FLAG} takes an absolute path (got ${JSON.stringify(value)})`
75362
+ );
75363
+ return;
75364
+ }
75365
+ paths.push(value);
75366
+ }
74383
75367
  const FRAME_LABEL = /^[a-z0-9][a-z0-9_-]{0,63}$/;
74384
75368
  function registerDevDriverIPC(options) {
74385
75369
  const { window, expectedUrl, outDir, identity: identity2 } = options;
@@ -83896,18 +84880,18 @@ function frameNavigationVerdict(input, trustedRendererUrls) {
83896
84880
  return ALLOW;
83897
84881
  return deny(`a child frame may not navigate to ${url.protocol}`);
83898
84882
  }
83899
- function permissionVerdict(query, trustedRendererUrls) {
83900
- if (!query.isMainFrame) return false;
84883
+ function permissionVerdict(query2, trustedRendererUrls) {
84884
+ if (!query2.isMainFrame) return false;
83901
84885
  if (!trustedRendererUrls.some(
83902
- (trusted) => trustedDesktopFrame(query.requestingUrl, trusted)
84886
+ (trusted) => trustedDesktopFrame(query2.requestingUrl, trusted)
83903
84887
  ))
83904
84888
  return false;
83905
- if (query.permission === "clipboard-sanitized-write" || query.permission === "fullscreen")
84889
+ if (query2.permission === "clipboard-sanitized-write" || query2.permission === "fullscreen")
83906
84890
  return true;
83907
- if (query.permission !== "media") return false;
83908
- if (query.mediaTypes)
83909
- return query.mediaTypes.length > 0 && query.mediaTypes.every((type2) => type2 === "audio");
83910
- return query.mediaType === "audio";
84891
+ if (query2.permission !== "media") return false;
84892
+ if (query2.mediaTypes)
84893
+ return query2.mediaTypes.length > 0 && query2.mediaTypes.every((type2) => type2 === "audio");
84894
+ return query2.mediaType === "audio";
83911
84895
  }
83912
84896
  function decodedPathname(pathname) {
83913
84897
  let current = pathname;
@@ -84725,6 +85709,13 @@ const devDriverArming = resolveDevDriverArming({
84725
85709
  });
84726
85710
  const devDriverLine = describeDevDriverArming(devDriverArming);
84727
85711
  if (devDriverLine) console.log(devDriverLine);
85712
+ const attachQueue = devDriverArming.armed ? readAttachArguments(process.argv) : { paths: [], problems: [] };
85713
+ for (const problem of attachQueue.problems)
85714
+ console.error(`[dev-driver] ${problem}`);
85715
+ if (devDriverArming.armed && attachQueue.paths.length > 0)
85716
+ console.log(
85717
+ `[dev-driver] attach queue: ${attachQueue.paths.length} path(s) will answer the next file dialog`
85718
+ );
84728
85719
  const addressOverlayGate = resolveAddressOverlayGate({
84729
85720
  env: launchEnv,
84730
85721
  devDriverArmed: devDriverArming.armed,
@@ -85550,6 +86541,24 @@ electron.app.whenReady().then(async () => {
85550
86541
  }
85551
86542
  }
85552
86543
  );
86544
+ electron.ipcMain.handle(
86545
+ "upload-attachment",
86546
+ async (event, source) => {
86547
+ if (!isAdmittedSender(event, trustedLocalFileSenders())) {
86548
+ return {
86549
+ success: false,
86550
+ code: "unreadable",
86551
+ error: "This window cannot read local files."
86552
+ };
86553
+ }
86554
+ const resolved = source.kind === "path" ? { ...source, path: resolveUserPath(source.path) } : source;
86555
+ return uploadAttachment({
86556
+ source: resolved,
86557
+ backendUrl: backendService.getBackendUrl(),
86558
+ token: backendService.getDesktopToken()
86559
+ });
86560
+ }
86561
+ );
85553
86562
  electron.ipcMain.handle("open-external", async (_, url) => openExternalVetted(url));
85554
86563
  electron.ipcMain.handle(
85555
86564
  "show-item-in-folder",
@@ -85595,6 +86604,13 @@ electron.app.whenReady().then(async () => {
85595
86604
  }
85596
86605
  });
85597
86606
  electron.ipcMain.handle("show-open-dialog", async (event, options) => {
86607
+ if (attachQueue.paths.length > 0) {
86608
+ const filePaths = attachQueue.paths.splice(0);
86609
+ console.log(
86610
+ `[dev-driver] answered the file dialog with ${filePaths.length} queued path(s)`
86611
+ );
86612
+ return { canceled: false, filePaths };
86613
+ }
85598
86614
  const parent = electron.BrowserWindow.fromWebContents(event.sender) ?? mainWindow;
85599
86615
  if (!parent) {
85600
86616
  logger.error(