agentbox-app 0.1.12 → 0.1.16

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 (370) hide show
  1. package/README.md +24 -9
  2. package/bin/agentbox.mjs +23 -1
  3. package/briefs/message-rules.md +36 -8
  4. package/briefs/worker.md +46 -23
  5. package/main/account-discovery.mjs +122 -30
  6. package/main/account-tooling.mjs +29 -1
  7. package/main/account-usage.mjs +46 -0
  8. package/main/acp.mjs +296 -0
  9. package/main/agent-homes.mjs +158 -0
  10. package/main/agent-sessions.mjs +311 -260
  11. package/main/agent-updates.mjs +9 -22
  12. package/main/agents.mjs +179 -466
  13. package/main/analytics.mjs +42 -2
  14. package/main/answer-modes.mjs +103 -37
  15. package/main/approval-prompt-core.mjs +197 -0
  16. package/main/approval-prompt-server.mjs +4 -138
  17. package/main/approvals.mjs +101 -297
  18. package/main/artifact-path.mjs +47 -18
  19. package/main/claude-bin.mjs +45 -311
  20. package/main/claude-input.mjs +57 -7
  21. package/main/claude-launch-env.mjs +54 -0
  22. package/main/claude-models.mjs +39 -7
  23. package/main/claude-plan.mjs +64 -16
  24. package/main/claude-session-open.mjs +28 -0
  25. package/main/claude-usage-process.mjs +40 -0
  26. package/main/claude-usage.mjs +94 -59
  27. package/main/cloud/account.mjs +179 -0
  28. package/main/cloud/aws.mjs +127 -0
  29. package/main/cloud/browser-return.mjs +58 -0
  30. package/main/cloud/clients.mjs +15 -0
  31. package/main/cloud/codex-cloud.mjs +53 -0
  32. package/main/cloud/config.mjs +107 -0
  33. package/main/cloud/docs.mjs +219 -0
  34. package/main/cloud/exec.mjs +27 -0
  35. package/main/cloud/host.mjs +165 -0
  36. package/main/cloud/kit.mjs +80 -0
  37. package/main/cloud/logins.mjs +543 -0
  38. package/main/cloud/managed.mjs +115 -0
  39. package/main/cloud/marker.mjs +104 -0
  40. package/main/cloud/org.mjs +53 -0
  41. package/main/cloud/reuse.mjs +208 -0
  42. package/main/cloud/run-cli.mjs +177 -0
  43. package/main/cloud/run.mjs +1934 -0
  44. package/main/cloud/usage.mjs +76 -0
  45. package/main/cloud-permission.mjs +51 -0
  46. package/main/codex-account.mjs +78 -0
  47. package/main/codex-app-server.mjs +276 -409
  48. package/main/codex-approvals.mjs +148 -433
  49. package/main/codex-bin.mjs +51 -249
  50. package/main/codex-models.mjs +20 -2
  51. package/main/codex-session.mjs +246 -506
  52. package/main/codex-threads-worker.mjs +12 -0
  53. package/main/codex-threads.mjs +112 -11
  54. package/main/codex-usage-file.mjs +5 -3
  55. package/main/codex-usage.mjs +56 -7
  56. package/main/codex-watch.mjs +83 -5
  57. package/main/codex.mjs +95 -363
  58. package/main/config.mjs +37 -5
  59. package/main/corner-tag.mjs +810 -0
  60. package/main/crash-report.mjs +26 -3
  61. package/main/dev-window.mjs +1 -1
  62. package/main/engine-bin.mjs +545 -0
  63. package/main/engine-setup.mjs +25 -24
  64. package/main/feedback.mjs +14 -31
  65. package/main/folder-pictures.mjs +2 -2
  66. package/main/freeze-recorder-thread.mjs +86 -0
  67. package/main/freeze-recorder.mjs +146 -0
  68. package/main/fresh-user.mjs +20 -9
  69. package/main/git-change-offthread.mjs +17 -3
  70. package/main/git-change-thread.mjs +5 -2
  71. package/main/harnesses/claude-stream.mjs +208 -0
  72. package/main/harnesses/claude.mjs +105 -0
  73. package/main/harnesses/codex.mjs +72 -0
  74. package/main/harnesses/common.mjs +78 -0
  75. package/main/harnesses/instructions-file.mjs +116 -0
  76. package/main/harnesses/opencode.mjs +234 -0
  77. package/main/harnesses.mjs +65 -0
  78. package/main/importable-threads.mjs +96 -0
  79. package/main/ipc.mjs +520 -135
  80. package/main/leftovers.mjs +117 -25
  81. package/main/liquid-glass.mjs +26 -0
  82. package/main/live-replies.mjs +10 -4
  83. package/main/local-app-frames.mjs +82 -0
  84. package/main/main-went-red.mjs +330 -0
  85. package/main/main.mjs +248 -37
  86. package/main/memory-gate-history.mjs +85 -20
  87. package/main/memory-gate-server.mjs +129 -11
  88. package/main/memory-gate.mjs +62 -8
  89. package/main/memory-log.mjs +124 -0
  90. package/main/menu.mjs +4 -2
  91. package/main/message-priority.mjs +2 -2
  92. package/main/mic.mjs +38 -0
  93. package/main/native/liquid-glass/build/liquid-glass.node +0 -0
  94. package/main/new-chats-watch.mjs +110 -0
  95. package/main/new-user-window.mjs +25 -0
  96. package/main/notify.mjs +64 -5
  97. package/main/opencode-server.mjs +271 -0
  98. package/main/opencode.mjs +287 -0
  99. package/main/phone-alerts.mjs +117 -0
  100. package/main/phone-inbox.mjs +625 -0
  101. package/main/phone-link.mjs +571 -0
  102. package/main/phone-uploads.mjs +476 -0
  103. package/main/picture-shape.mjs +114 -0
  104. package/main/quit-sequence.mjs +143 -0
  105. package/main/refresh-agent-models.mjs +15 -2
  106. package/main/relay-address.mjs +28 -0
  107. package/main/relay-client.mjs +314 -0
  108. package/main/repeats.mjs +10 -4
  109. package/main/row-label.mjs +18 -47
  110. package/main/run-change.mjs +13 -0
  111. package/main/run-ledger.mjs +135 -0
  112. package/main/runs-on.mjs +32 -35
  113. package/main/save-attachments.mjs +41 -0
  114. package/main/serve.mjs +155 -9
  115. package/main/settings.mjs +364 -50
  116. package/main/shell-path.mjs +234 -0
  117. package/main/ship-conflict-brief.mjs +119 -0
  118. package/main/ship-queue.mjs +134 -20
  119. package/main/sign-in-files.mjs +3 -7
  120. package/main/source-updater.mjs +45 -11
  121. package/main/stage-attachment.mjs +68 -0
  122. package/main/stall-watch.mjs +50 -0
  123. package/main/start-hold.mjs +73 -0
  124. package/main/store/home.mjs +87 -232
  125. package/main/store/paths.mjs +11 -0
  126. package/main/store/teammates-files.mjs +99 -0
  127. package/main/store/work-items.mjs +231 -13
  128. package/main/store.mjs +227 -53
  129. package/main/supervisor.mjs +3865 -699
  130. package/main/task-commands.mjs +13 -17
  131. package/main/task-folders-offthread.mjs +6 -1
  132. package/main/task-folders.mjs +897 -105
  133. package/main/task-terminals.mjs +147 -41
  134. package/main/team/index.mjs +114 -10
  135. package/main/team/memory-cloud.mjs +183 -1
  136. package/main/team/room-hours.mjs +48 -0
  137. package/main/team/room-setup.mjs +31 -0
  138. package/main/team/rooms-memory.mjs +454 -0
  139. package/main/team/rooms-shape.mjs +42 -0
  140. package/main/team/rooms-supabase.mjs +324 -0
  141. package/main/team/rooms.mjs +575 -0
  142. package/main/team/session.mjs +96 -26
  143. package/main/team/supabase-backend.mjs +180 -0
  144. package/main/team/sync.mjs +109 -1
  145. package/main/teammates.mjs +580 -0
  146. package/main/terminal-host.mjs +143 -0
  147. package/main/terminal-hosts.mjs +261 -0
  148. package/main/transcript-search.mjs +80 -0
  149. package/main/updater.mjs +71 -9
  150. package/main/webview-guard.mjs +45 -0
  151. package/main/worktree-include.mjs +365 -0
  152. package/mcp/core/documents.mjs +3 -1
  153. package/mcp/core/repeats.mjs +88 -0
  154. package/mcp/core/teammates.mjs +222 -0
  155. package/mcp/core/work.mjs +76 -4
  156. package/mcp/stdio.mjs +24 -11
  157. package/mcp/tools.mjs +116 -8
  158. package/package.json +30 -5
  159. package/renderer/dist/assets/abnfDiagram-YUKMZFIV-CoWB6YUK.js +1 -0
  160. package/renderer/dist/assets/arc-Ch0NrCul.js +1 -0
  161. package/renderer/dist/assets/architectureDiagram-47P4ROYG-CXz9M5tQ.js +36 -0
  162. package/renderer/dist/assets/blockDiagram-E7TT5TSB-DchGUuHH.js +140 -0
  163. package/renderer/dist/assets/c4Diagram-CRA5TL53-Cip-zNyr.js +38 -0
  164. package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
  165. package/renderer/dist/assets/chunk-2Q5K7J3B-BoXid_2r.js +1 -0
  166. package/renderer/dist/assets/chunk-5VM5RSS4-Cpez1rRz.js +15 -0
  167. package/renderer/dist/assets/chunk-7TKQ45FW-DKA84YkE.js +1 -0
  168. package/renderer/dist/assets/chunk-AW2ZBBNX-Dg-SumPG.js +88 -0
  169. package/renderer/dist/assets/chunk-FVRAUYC3-06TCvPm1.js +2 -0
  170. package/renderer/dist/assets/chunk-HTAEGDNF-CMEBy1Td.js +1 -0
  171. package/renderer/dist/assets/chunk-JWPE2WC7-BJ2ObmV-.js +1 -0
  172. package/renderer/dist/assets/chunk-KQW6MTUR-CGWOYXUE.js +1 -0
  173. package/renderer/dist/assets/chunk-XXDRQBXY-CiZp07XC.js +1 -0
  174. package/renderer/dist/assets/classDiagram-v2-K4WV4PDN-DJNIX719.js +217 -0
  175. package/renderer/dist/assets/cornerTag-CPgvu-q3.js +1 -0
  176. package/renderer/dist/assets/cornerTag-oMfEjzCO.css +1 -0
  177. package/renderer/dist/assets/cose-bilkent-JH36ORCC-CbkyZOUH.js +1 -0
  178. package/renderer/dist/assets/cynefin-EF2NZ3EQ-78iOroZO.js +141 -0
  179. package/renderer/dist/assets/cynefinDiagram-3GCD6N5R-GcCopMs7.js +62 -0
  180. package/renderer/dist/assets/cytoscape.esm-C5JX3QLV.js +331 -0
  181. package/renderer/dist/assets/dagre-W4DXFKR2-DHLlXLkz.js +4 -0
  182. package/renderer/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  183. package/renderer/dist/assets/diagram-2UJZ2QOL-DuZSUHcH.js +200 -0
  184. package/renderer/dist/assets/diagram-OVF4WLC6-Ckxu3cqH.js +24 -0
  185. package/renderer/dist/assets/diagram-PFPMY2P6-CsTw9Ycn.js +30 -0
  186. package/renderer/dist/assets/diagram-UMYRVEAY-CTXd8qVg.js +24 -0
  187. package/renderer/dist/assets/diagram-YEKJPTXX-CJs1T_9m.js +41 -0
  188. package/renderer/dist/assets/diagram-ZIFT7M5P-CvjI23mf.js +3 -0
  189. package/renderer/dist/assets/ebnfDiagram-VR2GEFS7-d_IeAzOJ.js +1 -0
  190. package/renderer/dist/assets/elk-IJKZMXRS-zo9KFP3N.js +27 -0
  191. package/renderer/dist/assets/erDiagram-O2IAPWRE-DqfYf5Dt.js +99 -0
  192. package/renderer/dist/assets/flowDiagram-OXPTDLAJ-DAWsMLmo.js +213 -0
  193. package/renderer/dist/assets/ganttDiagram-R7TSEDQI-4ml_bhwR.js +292 -0
  194. package/renderer/dist/assets/gitGraphDiagram-XJZIOB7I-B_WcqSId.js +106 -0
  195. package/renderer/dist/assets/graph-DGbsTEsI.js +1 -0
  196. package/renderer/dist/assets/infoDiagram-5W2HQ5XZ-CI-jYqzc.js +2 -0
  197. package/renderer/dist/assets/init-Gi6I4Gst.js +1 -0
  198. package/renderer/dist/assets/ishikawaDiagram-K3B6WC7H-B56VWRlV.js +70 -0
  199. package/renderer/dist/assets/journeyDiagram-COXZFDF6-D1-p09Lr.js +139 -0
  200. package/renderer/dist/assets/kanban-definition-P3RFRI5V-Cv3w7JoN.js +89 -0
  201. package/renderer/dist/assets/katex-C5jXJg4s.js +257 -0
  202. package/renderer/dist/assets/linear-C35V2Spi.js +1 -0
  203. package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
  204. package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
  205. package/renderer/dist/assets/mermaid.core-BzGT5Vo9.js +314 -0
  206. package/renderer/dist/assets/mindmap-definition-LZFPQGKD-BZUfuymC.js +96 -0
  207. package/renderer/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  208. package/renderer/dist/assets/pegDiagram-BGZESJAR-DFh9XNGe.js +1 -0
  209. package/renderer/dist/assets/pieDiagram-CAPJLFHJ-C65tPVsy.js +39 -0
  210. package/renderer/dist/assets/product-name-CSNW6d7s.js +40 -0
  211. package/renderer/dist/assets/quadrantDiagram-GDMTTRAM-Cushj_nk.js +7 -0
  212. package/renderer/dist/assets/railroadDiagram-SM67HX2B-DZGebGx5.js +1 -0
  213. package/renderer/dist/assets/requirementDiagram-X7JNWC4A-DpAB0MNo.js +84 -0
  214. package/renderer/dist/assets/sankeyDiagram-UM26HJRW-D-29rHra.js +40 -0
  215. package/renderer/dist/assets/sequenceDiagram-ZO4K6R2Y-FfOQAZol.js +169 -0
  216. package/renderer/dist/assets/sizeCapture-INFHLROL-C_n6fDBY.js +1 -0
  217. package/renderer/dist/assets/stateDiagram-v2-TBUQTH76-BZjenzJG.js +291 -0
  218. package/renderer/dist/assets/swimlanes-N4OXWK64-DxaMU9vX.js +1 -0
  219. package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
  220. package/renderer/dist/assets/timeline-definition-YOQAKGHF-DTg5HJKz.js +120 -0
  221. package/renderer/dist/assets/usecaseDiagram-VIAY4XPW-Bc3MZDsl.js +370 -0
  222. package/renderer/dist/assets/vennDiagram-BLWOH2XV-GIVGk9Id.js +34 -0
  223. package/renderer/dist/assets/wardleyDiagram-YMQ3BMBF-NmJnbQOg.js +78 -0
  224. package/renderer/dist/assets/xychartDiagram-TAQBALBS-p-kfGfnW.js +7 -0
  225. package/renderer/dist/corner-tag.html +13 -0
  226. package/renderer/dist/emulator/audio-rules.mjs +74 -0
  227. package/renderer/dist/emulator/data/compression/README.md +11 -0
  228. package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
  229. package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
  230. package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
  231. package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
  232. package/renderer/dist/emulator/data/cores/README.md +22 -0
  233. package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
  234. package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
  235. package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
  236. package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
  237. package/renderer/dist/emulator/data/cores/package.json +65 -0
  238. package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
  239. package/renderer/dist/emulator/data/emulator.css +1614 -0
  240. package/renderer/dist/emulator/data/loader.js +170 -0
  241. package/renderer/dist/emulator/data/localization/README.md +63 -0
  242. package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
  243. package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
  244. package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
  245. package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
  246. package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
  247. package/renderer/dist/emulator/data/localization/en-US.json +339 -0
  248. package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
  249. package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
  250. package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
  251. package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
  252. package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
  253. package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
  254. package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
  255. package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
  256. package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
  257. package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
  258. package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
  259. package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
  260. package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
  261. package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
  262. package/renderer/dist/emulator/data/src/GameManager.js +464 -0
  263. package/renderer/dist/emulator/data/src/compression.js +142 -0
  264. package/renderer/dist/emulator/data/src/emulator.js +6158 -0
  265. package/renderer/dist/emulator/data/src/gamepad.js +167 -0
  266. package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
  267. package/renderer/dist/emulator/data/src/shaders.js +236 -0
  268. package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
  269. package/renderer/dist/emulator/data/src/storage.js +125 -0
  270. package/renderer/dist/emulator/data/version.json +5 -0
  271. package/renderer/dist/emulator/host.js +297 -0
  272. package/renderer/dist/emulator/index.html +28 -0
  273. package/renderer/dist/emulator/provenance.json +6 -0
  274. package/renderer/dist/index.html +16 -3
  275. package/scripts/after-install.mjs +30 -8
  276. package/scripts/approval-server.sh +24 -5
  277. package/scripts/before-publish.mjs +12 -10
  278. package/scripts/cloud-shim.pl +57 -0
  279. package/shared/account-names.d.mts +6 -0
  280. package/shared/account-names.mjs +15 -0
  281. package/shared/agent-hours.mjs +136 -0
  282. package/shared/agent-import.mjs +62 -34
  283. package/shared/agents.d.mts +1 -0
  284. package/shared/agents.mjs +126 -522
  285. package/shared/analytics-events.mjs +12 -0
  286. package/shared/approval-card.mjs +36 -0
  287. package/shared/bridge-map.mjs +45 -1
  288. package/shared/claude-code-shape.mjs +6 -0
  289. package/shared/claude-commands.generated.mjs +6 -4
  290. package/shared/claude-models.generated.mjs +6 -4
  291. package/shared/claude-usage.d.mts +1 -0
  292. package/shared/claude-usage.mjs +42 -1
  293. package/shared/cloud-hand-back.d.mts +7 -0
  294. package/shared/cloud-hand-back.mjs +20 -0
  295. package/shared/cloud-kit.d.mts +219 -0
  296. package/shared/cloud-kit.mjs +341 -0
  297. package/shared/cloud-login-name.mjs +4 -0
  298. package/shared/cloud-start.mjs +17 -0
  299. package/shared/cloud-versions.mjs +23 -0
  300. package/shared/codex-import.mjs +2 -2
  301. package/shared/codex-modes.d.mts +22 -9
  302. package/shared/codex-modes.mjs +67 -11
  303. package/shared/codex-usage.mjs +108 -2
  304. package/shared/corner-tag.d.mts +36 -0
  305. package/shared/corner-tag.mjs +396 -0
  306. package/shared/dead-run-trace.mjs +19 -2
  307. package/shared/engines.mjs +76 -300
  308. package/shared/feedback.d.mts +7 -0
  309. package/shared/feedback.mjs +25 -0
  310. package/shared/first-run-practice.mjs +17 -24
  311. package/shared/harness-definitions.mjs +111 -0
  312. package/shared/instruction-defaults.json +2 -2
  313. package/shared/key-caps.d.mts +14 -0
  314. package/shared/key-caps.mjs +68 -0
  315. package/shared/message-edits.d.mts +7 -0
  316. package/shared/message-edits.mjs +35 -0
  317. package/shared/move-thread.d.mts +24 -0
  318. package/shared/move-thread.mjs +112 -0
  319. package/shared/named-threads.mjs +72 -0
  320. package/shared/not-shipped.d.mts +22 -0
  321. package/shared/not-shipped.mjs +89 -0
  322. package/shared/notify-rules.d.mts +31 -0
  323. package/shared/notify-rules.mjs +55 -8
  324. package/shared/phone-files.mjs +28 -0
  325. package/shared/phone-settings.mjs +118 -0
  326. package/shared/phone-snapshot.d.mts +16 -0
  327. package/shared/phone-snapshot.mjs +348 -0
  328. package/shared/picture-formats.d.mts +1 -0
  329. package/shared/picture-formats.mjs +13 -0
  330. package/shared/provider-commands.mjs +10 -2
  331. package/shared/rank.d.mts +4 -1
  332. package/shared/rank.mjs +82 -19
  333. package/shared/relay-address.mjs +6 -0
  334. package/shared/relay-crypto.mjs +165 -0
  335. package/shared/repeats.d.mts +8 -0
  336. package/shared/repeats.mjs +33 -2
  337. package/shared/room-cards.mjs +167 -0
  338. package/shared/room-links.d.mts +4 -0
  339. package/shared/room-links.mjs +21 -0
  340. package/shared/runs-on.d.mts +3 -13
  341. package/shared/runs-on.mjs +31 -52
  342. package/shared/shipped-note.d.mts +4 -0
  343. package/shared/shipped-note.mjs +6 -0
  344. package/shared/shortcuts.mjs +24 -10
  345. package/shared/spawn-trouble.d.mts +4 -0
  346. package/shared/spawn-trouble.mjs +484 -30
  347. package/shared/team-rules.mjs +9 -2
  348. package/shared/teammates.d.mts +19 -0
  349. package/shared/teammates.mjs +756 -0
  350. package/shared/terminal-focus.d.mts +1 -0
  351. package/shared/terminal-focus.mjs +13 -0
  352. package/shared/terminal-input.d.mts +1 -0
  353. package/shared/terminal-input.mjs +30 -0
  354. package/shared/terminal-keys.d.mts +1 -0
  355. package/shared/terminal-keys.mjs +16 -0
  356. package/shared/terminal-size.d.mts +4 -0
  357. package/shared/terminal-size.mjs +29 -0
  358. package/shared/thread-cards.mjs +4 -1
  359. package/shared/usage.d.mts +12 -0
  360. package/shared/usage.mjs +56 -3
  361. package/shared/websites.d.mts +15 -0
  362. package/shared/websites.mjs +61 -0
  363. package/shared/where-it-runs-from.d.mts +43 -0
  364. package/shared/where-it-runs-from.mjs +155 -0
  365. package/shared/work-items.mjs +150 -29
  366. package/shared/work-lines.mjs +93 -245
  367. package/shared/xterm-invalid-range.d.mts +2 -0
  368. package/shared/xterm-invalid-range.mjs +58 -0
  369. package/renderer/dist/assets/index-ChKsQkRw.js +0 -1169
  370. package/renderer/dist/assets/index-ClBXnq3B.css +0 -32
package/main/main.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  // The app: the agent inbox. Electron shell around a keyboard-first renderer, a
2
2
  // file-derived store, and a supervisor of headless sessions.
3
3
 
4
- import { app, BrowserWindow, Menu, Notification, clipboard, crashReporter, dialog, ipcMain, nativeImage, net, powerMonitor, protocol, screen as electronScreen, shell, session, safeStorage } from 'electron';
4
+ import { app, BrowserWindow, Menu, Notification, clipboard, crashReporter, dialog, ipcMain, nativeImage, net, powerMonitor, protocol, screen as electronScreen, shell, session, safeStorage, systemPreferences } from 'electron';
5
5
  import * as workItemsDisk from './store/work-items.mjs';
6
6
  import { createTeamService, teamStateFile } from './team/index.mjs';
7
+ import { roomsSetup } from './team/room-setup.mjs';
7
8
  import { loadCloudConfig, supabaseSession } from './team/session.mjs';
8
9
  // MY WORKSPACE IS NOT A TEAM FEATURE ANY MORE (w-f8d123be62), even though it
9
10
  // still lives in the team folder next to the sharing rules it has to obey.
@@ -11,34 +12,52 @@ import { ensurePersonalProject } from './team/projects.mjs';
11
12
  import path from 'node:path';
12
13
  import fs from 'node:fs';
13
14
  import { fileURLToPath, pathToFileURL } from 'node:url';
14
- import { configDir, loadConfig } from './config.mjs';
15
+ import { configDir, loadConfig, saveConfig } from './config.mjs';
15
16
  import { installMenu, applyZoom, requestFind } from './menu.mjs';
16
17
  import { zoomDeltaFor } from './zoom-keys.mjs';
17
18
  import { copySelection } from './copy-selection.mjs';
18
19
  import { screenDetailFor } from './screen-detail.mjs';
19
20
  import { Store } from './store.mjs';
20
21
  import { storeRootEnv } from './store/home.mjs';
22
+ import { keepAnswerModesIn } from './answer-modes.mjs';
21
23
  import { Supervisor } from './supervisor.mjs';
22
24
  import { startCodexWatch } from './codex-watch.mjs';
25
+ import { watchNewChatsFor } from './new-chats-watch.mjs';
26
+ import { readCodexThreadsOffThread } from './codex-threads.mjs';
27
+ import { appSessionFolders } from './agent-homes.mjs';
23
28
  import { registerIpc } from './ipc.mjs';
29
+ import { stallWatch } from './stall-watch.mjs';
30
+ import { startFreezeRecorder } from './freeze-recorder.mjs';
31
+ import { applyThrough, phoneDevice } from './phone-inbox.mjs';
32
+ import { answerWhatOnlyTheWindowAnswered } from './serve.mjs';
24
33
  import { carryHerBriefsAcross, joinMessageRules, setAsideShippedMessageRules, unifyAgentInstructions } from './instruction-settings.mjs';
25
34
  import * as approvals from './approvals.mjs';
26
35
  import { recoveryToast } from '../shared/recovery.mjs';
27
36
  import { dataFolderName, isNewUserBuild } from '../shared/side-build.mjs';
28
37
  import { runningAsAFreshUser } from '../shared/fresh-user-home.mjs';
38
+ import { newUserWindow } from './new-user-window.mjs';
29
39
  import { openFreshUser } from './fresh-user.mjs';
30
40
  import { installCrashReports, reportFromRenderer, pending as pendingCrashes, setTransport } from './crash-report.mjs';
31
41
  import { createAnalytics, createDailyCount } from './analytics.mjs';
32
42
  import { createUpdater } from './updater.mjs';
43
+ import { quitStep, flushBeforeQuit, quitThenExit } from './quit-sequence.mjs';
44
+ import { createPlace } from '../shared/where-it-runs-from.mjs';
33
45
  import { createSourceUpdater } from './source-updater.mjs';
46
+ import { learnShellDataDirs, recoverShellPath } from './shell-path.mjs';
34
47
  import { installNotifier } from './notify.mjs';
48
+ import { createCornerTag } from './corner-tag.mjs';
49
+ import { loadLiquidGlass } from './liquid-glass.mjs';
35
50
  import { DOC_SCHEMES, DocGrants, docPath } from './doc-scheme.mjs';
36
51
  import { quietTheFramesScrollbars } from './frame-scrollbars.mjs';
52
+ import { letLocalAppsShowInThePane } from './local-app-frames.mjs';
53
+ import { hardenAttachedWebview } from './webview-guard.mjs';
37
54
  import { IMG_SCHEMES, imgPath, mediaResponse, mediaType, servable } from './img-scheme.mjs';
38
55
  import { hotWindowVerdict, storeHasWork } from './dev-window.mjs';
39
56
  import { writeHeldThenReload } from './write-before-reload.mjs';
40
57
  import { IS_SHE_TYPING_IN_THE_PAGE, theAppKeepsThisInput, theAppKeepsThisLetterInAPage } from '../shared/artifact-keys.mjs';
41
58
  import { NAME, Name, WAS } from '../shared/product-name.mjs';
59
+ import { JOIN_SCHEME, ourLink, tokenFromLink } from '../shared/room-links.mjs';
60
+ import { getMic } from './mic.mjs';
42
61
 
43
62
  // HOW LONG TO WAIT AFTER THE LID OPENS, and the wait is for the network rather
44
63
  // than for the wake. `powerMonitor`'s resume event fires while wifi is still
@@ -228,6 +247,24 @@ installCrashReports({
228
247
  });
229
248
 
230
249
  let window = null;
250
+ // A ROOM INVITE LINK THAT OPENED THE APP (Rooms, alpha): agentbox://join/<token>.
251
+ // Held until the window takes it ('zero:take-join'); a window that is up is
252
+ // told to come and take it, and one still starting takes it as it mounts.
253
+ let pendingJoin = tokenFromLink(process.argv.find((a) => a.startsWith(`${JOIN_SCHEME}://`)) ?? '');
254
+ function showWindow() {
255
+ if (!window || window.isDestroyed()) return;
256
+ if (window.isMinimized()) window.restore();
257
+ window.focus();
258
+ }
259
+ function openJoinLink(url) {
260
+ const token = tokenFromLink(url);
261
+ if (!token) return;
262
+ pendingJoin = token;
263
+ if (window && !window.isDestroyed()) {
264
+ window.webContents.send('zero:join-room');
265
+ showWindow();
266
+ }
267
+ }
231
268
  // Set by ⌘R and read once by the renderer as it comes back up, so a reload can
232
269
  // say so. A reload that lands on an identical screen and a chord that did
233
270
  // nothing at all look exactly alike, and telling them apart is the whole
@@ -275,12 +312,23 @@ function reloadRenderer() {
275
312
  }
276
313
 
277
314
  async function createWindow() {
315
+ // Where the shell says Claude Code and Codex keep their logins and sessions,
316
+ // never put into this environment (main/shell-path.mjs). WAITED FOR, beside
317
+ // the PATH read so it costs no extra time: the folders it names are where
318
+ // workers sign in now (w-6d8c543a43), and a task resumed at launch before
319
+ // the answer landed ran on ~/.claude signed out.
320
+ const [shellPath] = await Promise.all([recoverShellPath(), learnShellDataDirs()]);
321
+ process.env.PATH = shellPath.path;
322
+ if (shellPath.error) console.warn('agentbox: could not load your shell PATH; using the inherited PATH:', shellPath.error);
278
323
  const config = loadConfig(dataDir);
279
324
  // The store modules read the app's own home variable at call time; setting it
280
325
  // here points auth session files, sync, and every store path at its root.
281
326
  // Written under every name this app has had, because a store server or a
282
327
  // script from before a rename is still out there reading the old one.
283
328
  Object.assign(process.env, storeRootEnv(config.storeRoot));
329
+ // What a reply may do is kept in her own folder, not that home, which every
330
+ // worker is granted (main/answer-modes.mjs).
331
+ keepAnswerModesIn({ userDir, storeRoot: config.storeRoot });
284
332
 
285
333
  // Menu needs the config (approval hotkeys answer the oldest pending card),
286
334
  // so it installs here rather than at whenReady. The spool watcher in ipc
@@ -294,11 +342,11 @@ async function createWindow() {
294
342
  // themselves with `approvals.answer`, and on a Codex card a file is not an
295
343
  // answer at all: the thing waiting is Agentbox itself, in this process, so
296
344
  // nothing on that path ever reads one. Her click on Allow landed as a
297
- // FORGERY -- the file appeared for a request still waiting, `sweepForgery`
298
- // deleted it inside two seconds, the log said "an answer file appeared for
299
- // approval <id> that the founder did not write", her card came back and the
300
- // worker was denied fifteen minutes later by the deadline. It also spent the
301
- // one warning that is supposed to mean a worker attacked the spool.
345
+ // FORGERY -- the file appeared for a request still waiting, the card's own
346
+ // poll deleted it inside two seconds, the log said "an answer file appeared
347
+ // for approval <id> that the founder did not write", and her card came back
348
+ // with the worker still parked on it. It also spent the one warning that is
349
+ // supposed to mean a worker attacked the spool.
302
350
  //
303
351
  // The variable is filled in the moment `registerIpc` returns, a few hundred
304
352
  // lines down. Until then there is nothing to answer with and nothing that
@@ -337,7 +385,7 @@ async function createWindow() {
337
385
  analytics.track('app_opened');
338
386
  usedToday(Date.now());
339
387
  } else {
340
- console.log(`zero: diagnostics is ${analytics.reason}, nothing is sent`);
388
+ console.log(`zero: diagnostics ${analytics.reason}, nothing is sent`);
341
389
  }
342
390
 
343
391
  // EVERYBODY HAS SOMEWHERE TO SEND A THREAD (w-f8d123be62, approved
@@ -362,7 +410,16 @@ async function createWindow() {
362
410
  const supervisor = new Supervisor(config, store, appDir, dataDir, userDir);
363
411
  // THAT agents start and stop work (main/analytics.mjs checks the switch).
364
412
  supervisor.count = (name, props) => analytics.track(name, props);
365
- app.on('before-quit', () => supervisor.killAll());
413
+ const stalls = stallWatch();
414
+ supervisor.stalls = stalls;
415
+ // AND WHICH OF OUR FUNCTIONS HELD THE THREAD, each time it froze.
416
+ const freezes = startFreezeRecorder({
417
+ dir: path.join(userDir, 'memory-log'),
418
+ appDir,
419
+ onFreeze: (f) => { stalls.noteFreeze(f); console.warn(`zero: the app froze for ${f.ms} ms in ${f.where}`); },
420
+ });
421
+ quitStep(app, 'before-quit', 'freezes.stop', () => freezes.stop());
422
+ quitStep(app, 'before-quit', 'supervisor.killAll', () => supervisor.killAll());
366
423
  // THE TEAM. Off entirely on a build with no team cloud (the single-person
367
424
  // app). Otherwise it restores whoever was signed in, and from then on every
368
425
  // line this Mac writes says who wrote it and shared projects stay in sync.
@@ -373,13 +430,16 @@ async function createWindow() {
373
430
  // single-person app, which has no sign-in to keep
374
431
  // (tests/the-installed-app-is-the-single-person-app.test.mjs).
375
432
  const cloudConfig = loadCloudConfig(appDir, { packaged: app.isPackaged });
433
+ let phoneApply = null;
434
+ let phoneSettings = null;
435
+ // Every installed build has a cloud now, so the keychain is asked only once
436
+ // a sign-in is read or kept, not at launch.
376
437
  const team = cloudConfig ? createTeamService({
377
438
  session: supabaseSession({
378
439
  cloudConfig,
379
440
  sessionFile: path.join(config.storeRoot, '.team-session'),
380
- ...(safeStorage.isEncryptionAvailable()
381
- ? { encrypt: (text) => safeStorage.encryptString(text), decrypt: (buf) => safeStorage.decryptString(buf) }
382
- : { encrypt: null, decrypt: null }),
441
+ encrypt: (text) => (safeStorage.isEncryptionAvailable() ? safeStorage.encryptString(text) : Buffer.from(text, 'utf8')),
442
+ decrypt: (buf) => (safeStorage.isEncryptionAvailable() ? safeStorage.decryptString(buf) : buf.toString('utf8')),
383
443
  packaged: app.isPackaged,
384
444
  openExternal: (url) => shell.openExternal(url),
385
445
  }),
@@ -391,18 +451,68 @@ async function createWindow() {
391
451
  // exists; until then a change has nobody to tell.
392
452
  onChange: () => { try { pushUpdate(); } catch { /* no window yet */ } },
393
453
  log: (line) => console.log(line),
454
+ // HER PHONE WITH THE MAC SHUT (main/phone-inbox.mjs). What it left is done
455
+ // through the window's own handlers, filed below once the window exists.
456
+ phone: {
457
+ // Her yes, with her id on it (shared/phone-snapshot.mjs phoneProjectsOf).
458
+ projects: () => config.phoneProducts ?? null,
459
+ ready: () => !!phoneApply,
460
+ apply: (cmd) => phoneApply(cmd),
461
+ // The Settings pages' model, read through the window's own handler.
462
+ readSettings: () => phoneSettings?.() ?? null,
463
+ version: app.getVersion(),
464
+ live: (slug, id) => supervisor.liveFor(slug, id),
465
+ // The cards waiting on her, each with the fingerprint her answer must carry back.
466
+ approvals: () => approvals.listPending(config.storeRoot).map((r) => ({ ...r, card: approvals.cardShown(config.storeRoot, r.id) })),
467
+ stateFile: path.join(config.storeRoot, '.phone-inbox.json'),
468
+ // This installation and this store together (main/phone-inbox.mjs).
469
+ device: () => phoneDevice(userDir, config.storeRoot),
470
+ // Where her phone reads it, once the site is up (cloud/team.config.json).
471
+ site: typeof cloudConfig.phoneSite === 'string' ? cloudConfig.phoneSite : null,
472
+ // No alert on her phone while she is at this Mac (main/phone-alerts.mjs).
473
+ idleMs: () => powerMonitor.getSystemIdleTime() * 1000,
474
+ // A reply she sends with the lid shut carries on in the cloud (shared/cloud-kit.mjs).
475
+ carrying: (c) => supervisor.phoneCarrying(c),
476
+ kitsFor: (slug) => supervisor.leavesKitsFor(slug),
477
+ adopt: ({ project, item, runId, replies }) => supervisor.adoptCloudRun({ product: project, itemId: item, runId, replies }),
478
+ cloudReplies: ({ project, item, replies }) => supervisor.cloudRunReplies({ product: project, itemId: item, replies }),
479
+ runHome: ({ project, item, runId }) => supervisor.cloudRunHome({ product: project, itemId: item, runId }),
480
+ // And a new thread she starts with the lid shut.
481
+ newThreadKits: (c) => supervisor.leaveNewThreadKits(c),
482
+ adoptNew: ({ project, item, ...rest }) => supervisor.adoptCloudNewThread({ product: project, itemId: item, ...rest }),
483
+ },
484
+ // ROOMS, while switched on in Alpha features (docs/team/ROOMS-PLAN.md).
485
+ rooms: roomsSetup({ config, store, supervisor, userDir }),
394
486
  }) : null;
395
487
  if (team) {
396
488
  team.start().then(() => supervisor.wake?.()).catch((err) => console.warn(`team: ${err.message}`));
397
- app.on('before-quit', () => team.stop());
489
+ quitStep(app, 'before-quit', 'team.stop', () => team.stop());
398
490
  }
399
491
  // HER CODEX CONVERSATIONS ASK TO COME IN. Once a minute: a new one in a
400
492
  // folder a project points at gets a row asking yes or no, and the ones she
401
493
  // said yes to follow Codex's latest answer.
402
- const codexWatch = startCodexWatch({ store });
403
- app.on('before-quit', () => codexWatch.stop());
404
- // Whatever is still queued goes with the app rather than dying in memory.
405
- app.on('will-quit', () => { analytics.flush(); });
494
+ // AND IT IS WHERE A CODEX AGENT IS COUNTED, because it is the one thing that
495
+ // already reads them (main/codex-watch.mjs). The switch is checked by
496
+ // `analytics.track` itself, so this hands it over unconditionally.
497
+ // Every Codex folder on the machine, read fresh each minute so a login made
498
+ // while the app is open is followed too (main/agent-homes.mjs).
499
+ const codexWatch = startCodexWatch({
500
+ store,
501
+ count: (name, props) => analytics.track(name, props),
502
+ readThreads: (opts) => readCodexThreadsOffThread({ ...opts, codexDirs: appSessionFolders({ config, supervisor }).codexDirs }),
503
+ });
504
+ quitStep(app, 'before-quit', 'codexWatch.stop', () => codexWatch.stop());
505
+ // New chats from Claude Code and Codex, once asked for on setup's last
506
+ // screen or in Settings. The watch checks the switch every pass.
507
+ const newChatsWatch = watchNewChatsFor({ store, config, supervisor });
508
+ quitStep(app, 'before-quit', 'newChatsWatch.stop', () => newChatsWatch.stop());
509
+ // Whatever is still queued goes with the app rather than dying in memory,
510
+ // and the quit WAITS for it (main/quit-sequence.mjs). It did not: the
511
+ // process went as soon as this returned, so the last batch was a POST that
512
+ // never left, which on an install that opens the app once is everything we
513
+ // would ever have heard from it (w-60c8ddbab6).
514
+ quitStep(app, 'before-quit', 'count the quit', () => analytics.track('app_quit', { seconds: Math.round(process.uptime()) }));
515
+ flushBeforeQuit(app, () => analytics.flush());
406
516
 
407
517
  // Right-click. Electron ships no context menu at all, so links could not be
408
518
  // copied or opened by hand anywhere in the app. Built per click from what is
@@ -593,9 +703,18 @@ async function createWindow() {
593
703
  window = new BrowserWindow({
594
704
  width: 1440,
595
705
  height: 900,
596
- minWidth: 980,
706
+ // Half of a 1440-point screen, so the window tiles; under 700 is the phone layout.
707
+ minWidth: 720,
597
708
  minHeight: 600,
598
- title: NAME,
709
+ // A throwaway copy says which one it is and stands a shoulder clear;
710
+ // your own window is centred and titled as it was.
711
+ ...newUserWindow({
712
+ fresh: runningAsAFreshUser(),
713
+ name: NAME,
714
+ screen: electronScreen.getPrimaryDisplay()?.workArea,
715
+ width: 1440,
716
+ height: 900,
717
+ }),
599
718
  backgroundColor: '#1a1a1c',
600
719
  titleBarStyle: 'hiddenInset',
601
720
  webPreferences: {
@@ -606,6 +725,11 @@ async function createWindow() {
606
725
  },
607
726
  });
608
727
 
728
+ // Electron hands the window's title to the page's the moment one loads, and
729
+ // renderer/index.html carries the app's name, so a throwaway copy went back
730
+ // to saying "Agentbox" as soon as it drew anything.
731
+ window.on('page-title-updated', (e) => e.preventDefault());
732
+
609
733
  // THE MENU IS A BAR INSIDE THE WINDOW on Linux and Windows. On macOS it is
610
734
  // the system menu, so it stays. The menu itself stays everywhere: the Ctrl
611
735
  // chords are bound to it, and taking the menu out would take them with it.
@@ -661,9 +785,16 @@ async function createWindow() {
661
785
  // AND A COPY RUN FROM SOURCE watches its git branch instead of a release
662
786
  // feed (main/source-updater.mjs), because that is how teammates run the team
663
787
  // build and their updates are pushes to main, not releases.
788
+ //
789
+ // AND WHERE THIS COPY IS RUNNING FROM (shared/where-it-runs-from.mjs). An
790
+ // app run out of the disk image, or out of a read-only copy macOS made of
791
+ // it, can never update itself, and until 2026-10-12 nothing in the app said
792
+ // so: two installs sat on 0.1.11 for a week. The fact rides the updater's
793
+ // state so the Settings row, the sidebar card and ⌘K all read one answer.
664
794
  let pushUpdate = () => {};
665
795
  const updater = app.isPackaged ? createUpdater({
666
796
  app,
797
+ place: createPlace({ app }),
667
798
  onChanged: () => pushUpdate(),
668
799
  }) : createSourceUpdater({
669
800
  app,
@@ -673,9 +804,24 @@ async function createWindow() {
673
804
 
674
805
  // The desktop door. `main/serve.mjs` is the other one and hands over plain
675
806
  // Node stand-ins for these three; everything else about the call is the same.
807
+ // What the phone leaves in the cloud is done through these same handlers
808
+ // (main/phone-inbox.mjs), so each one is filed in a Map as Electron gets it.
809
+ const phoneChannels = new Map();
810
+ const filingIpcMain = {
811
+ handle(channel, fn) { ipcMain.handle(channel, stalls.time(channel, fn)); phoneChannels.set(channel, fn); },
812
+ };
813
+ // `readClipboardText` is read only by the cloud setup, and only for a
814
+ // Claude sign-in code (main/ipc.mjs `zero:cloud-clipboard-code`).
815
+ const filingHost = {
816
+ ipcMain: filingIpcMain, app, dialog, readClipboardText: () => clipboard.readText(),
817
+ // A voice note asks for the microphone on the press of its button.
818
+ micAccess: () => (process.platform === 'darwin'
819
+ ? getMic({ systemPreferences, openExternal: (url) => shell.openExternal(url) })
820
+ : 'granted'),
821
+ };
676
822
  const ipc = registerIpc({
677
823
  store, supervisor, config, window, analytics, docGrants, updater,
678
- host: { ipcMain, app, dialog },
824
+ host: filingHost,
679
825
  team,
680
826
  });
681
827
  pushUpdate = ipc.push;
@@ -683,11 +829,17 @@ async function createWindow() {
683
829
  // See the `installMenu` call above for what writing the file instead did to a
684
830
  // Codex card.
685
831
  answerApproval = ipc.answerApproval;
832
+ // What the window answers itself (badge, boot info) gets the plain stand-ins
833
+ // a browser tab gets.
834
+ answerWhatOnlyTheWindowAnswered({ handle: (channel, fn) => { if (!phoneChannels.has(channel)) phoneChannels.set(channel, fn); } });
835
+ phoneApply = applyThrough(phoneChannels);
836
+ // Every minute, so no engine search: that is a login shell on this thread.
837
+ phoneSettings = () => phoneChannels.get('zero:settings-read')?.({ sender: null }, { search: false }) ?? null;
686
838
  updater.start();
687
839
  // A task the app just shipped moved main: look now rather than in half an
688
840
  // hour, so the restart is offered while she still remembers the task.
689
841
  supervisor.onShipped = () => { try { updater.check?.(); } catch {} };
690
- app.on('before-quit', () => updater.stop());
842
+ quitStep(app, 'before-quit', 'updater.stop', () => updater.stop());
691
843
 
692
844
  // Cmd+Y / Cmd+N answer the oldest approval card from ANYWHERE, including
693
845
  // while a child web contents owns the keyboard (menu accelerators turned out
@@ -863,7 +1015,31 @@ async function createWindow() {
863
1015
  // a locked screen all read the same from in there). The renderer says what
864
1016
  // arrived; main/notify.mjs decides. tell me when I am in another app, never
865
1017
  // while I am in Agentbox.
866
- installNotifier({ app, window, Notification, powerMonitor, ipcMain });
1018
+ // A banner and the tray never disagree: whatever a banner told you about
1019
+ // while you were away is in the tray until you are back (w-93b3a1a796).
1020
+ let cornerTag = null;
1021
+ const notifier = installNotifier({
1022
+ app, window, Notification, powerMonitor, ipcMain, nativeImage,
1023
+ onArrivals: (list, needsYou) => team?.phoneArrivals(list, needsYou),
1024
+ onChange: () => cornerTag?.refresh(),
1025
+ });
1026
+ // THE CORNER TAG (w-dafae58a23): what is ready for her, floating over
1027
+ // whatever app she is in. Its page is built beside the app's own, and comes
1028
+ // from the dev server only when the app's own page does.
1029
+ cornerTag = createCornerTag({
1030
+ BrowserWindow, screen: electronScreen, ipcMain, Menu, powerMonitor, config, saveConfig, mainWindow: window,
1031
+ preload: path.join(appDir, 'main', 'corner-tag-preload.cjs'),
1032
+ glass: loadLiquidGlass(),
1033
+ told: () => notifier.told(),
1034
+ count: (name, props) => analytics.track(name, props),
1035
+ // `part` is the tag itself or the list beside it, one page drawn twice.
1036
+ load: (tagWindow, part) => {
1037
+ const devTag = process.env.ZERO_DEV_URL;
1038
+ const builtTag = path.join(appDir, 'renderer', 'dist', 'corner-tag.html');
1039
+ if (devTag && !fs.existsSync(builtTag)) tagWindow.loadURL(`${devTag.replace(/\/$/, '')}/corner-tag.html?part=${part}`);
1040
+ else tagWindow.loadFile(builtTag, { search: `part=${part}` });
1041
+ },
1042
+ });
867
1043
  // Asked for once by the renderer as it mounts, rather than pushed on
868
1044
  // did-finish-load, which races the first effects and would drop the very
869
1045
  // message it exists to deliver.
@@ -880,6 +1056,7 @@ async function createWindow() {
880
1056
  // itself (it has no disk and should not have one), and 'render-process-gone'
881
1057
  // only fires when the process DIES, which a thrown React error does not.
882
1058
  ipcMain.handle('zero:crash', (_e, payload) => { reportFromRenderer(payload ?? {}); });
1059
+ ipcMain.handle('zero:take-join', () => { const token = pendingJoin; pendingJoin = null; return token; });
883
1060
  // External links from markdown open in the real browser; local artifacts
884
1061
  // (a demo page, a screenshot a worker saved) open in their default app, so
885
1062
  // "see it working" is one click from the message.
@@ -889,32 +1066,57 @@ async function createWindow() {
889
1066
  return { action: 'deny' };
890
1067
  });
891
1068
 
892
- // NO PAGE IN AN EMBEDDED BROWSER STARTS SOUND BEFORE SHE HAS CLICKED IN IT.
1069
+ // WHAT AN EMBEDDED PAGE MAY DO IS DECIDED HERE, NOT ON THE TAG THAT ASKED.
893
1070
  //
894
- // Electron's default is `no-user-gesture-required`, so any page may start
895
- // media the moment it loads, and the rest browser reloads her last tab at
896
- // every launch, out of sight. Measured 2026-09-10 on Electron 43 in a hidden
897
- // webview on a YouTube watch page: default, the video ran from 3s to 15s;
898
- // with this rule it sat paused at 0 for all 16s. A click anywhere in the page
899
- // is activation, so pressing play, or picking the next video inside YouTube,
900
- // works as before. Every webview this window attaches gets it, the junk
901
- // browser included, which is what "never" asks for.
902
- window.webContents.on('will-attach-webview', (_e, webPreferences) => {
903
- webPreferences.autoplayPolicy = 'document-user-activation-required';
1071
+ // The pane draws a website now, which is the first code in this window that
1072
+ // nobody here wrote, so every webview is pinned below the tag: no preload,
1073
+ // sandboxed, context-isolated, node off, web security on, and no sound
1074
+ // before a click. main/webview-guard.mjs is the rule and the reasons.
1075
+ window.webContents.on('will-attach-webview', (_e, webPreferences, params) => {
1076
+ hardenAttachedWebview(webPreferences, params);
904
1077
  });
905
1078
 
906
1079
  // A page in the pane hides its scrollbar until you scroll, like the app's own
907
1080
  // (frame-scrollbars.mjs). Without it, a mouse on macOS gets a white track.
908
1081
  quietTheFramesScrollbars(window.webContents);
909
1082
 
1083
+ // A local app a worker names (http://localhost:3006) draws in the pane even
1084
+ // when it refuses to be framed, and its sign-in cookie sticks there
1085
+ // (main/local-app-frames.mjs).
1086
+ letLocalAppsShowInThePane(window.webContents);
1087
+
910
1088
  // Escape must ALWAYS leave the junk browser, even when the guest page has
911
1089
  // focus and would otherwise swallow the key. Intercept it below the page.
912
1090
  app.on('web-contents-created', (_event, contents) => {
913
1091
  if (contents.getType() !== 'webview') return;
1092
+
1093
+ // A LINK IN A WEBSITE THAT WANTS A NEW WINDOW GOES TO YOUR OWN BROWSER.
1094
+ // The pane is one page at a time; a second Electron window would be a
1095
+ // browser we have not designed, and `allowpopups` is off anyway
1096
+ // (webview-guard.mjs), so without this such a link does nothing at all.
1097
+ contents.setWindowOpenHandler(({ url }) => {
1098
+ if (/^https?:/.test(url)) shell.openExternal(url);
1099
+ return { action: 'deny' };
1100
+ });
1101
+
914
1102
  contents.on('before-input-event', (e, input) => {
1103
+ // ESCAPE HAS TO REACH THE APP FROM INSIDE A PAGE, and a webview is not
1104
+ // a frame of this window, so the handler above never hears it. It comes
1105
+ // up the same channel a frame's does, because the renderer answers that
1106
+ // one and decides there what escape means (whatTheFileSentUp). It used
1107
+ // to be sent on `zero:escape-browser`, the junk browser's own channel,
1108
+ // which no renderer code has listened to since that pane went: the key
1109
+ // landed nowhere and the pane took two presses to close.
915
1110
  if (input.type === 'keyDown' && input.key === 'Escape' && window && !window.isDestroyed()) {
916
- window.webContents.send('zero:escape-browser');
917
- window.webContents.focus();
1111
+ // The keyboard is left where it is, exactly as it is for a frame: the
1112
+ // renderer blurs the page before the pane goes. Taking focus back
1113
+ // here first would move `document.activeElement` off the webview, and
1114
+ // the renderer's own check of the same fact would then refuse the key
1115
+ // that this just sent.
1116
+ window.webContents.send('zero:key-in-the-file', {
1117
+ key: input.key, meta: !!input.meta, ctrl: !!input.control, alt: !!input.alt, shift: !!input.shift,
1118
+ fromGuest: true,
1119
+ });
918
1120
  return;
919
1121
  }
920
1122
  // The guest page owns the keyboard while she is in the junk browser, so
@@ -997,7 +1199,9 @@ async function createWindow() {
997
1199
  } catch (err) {
998
1200
  console.error('screenshot failed:', err.message);
999
1201
  }
1000
- app.quit();
1202
+ // The picture is taken, so the run is over whether the quit works or
1203
+ // not. On Linux it did not, and the shell's `timeout` ended these runs.
1204
+ quitThenExit(app, { what: 'the screenshot run' });
1001
1205
  }, Number(process.env.ZERO_SCREENSHOT_DELAY_MS ?? 2500));
1002
1206
  }
1003
1207
 
@@ -1015,12 +1219,16 @@ async function createWindow() {
1015
1219
  if (!app.requestSingleInstanceLock()) {
1016
1220
  app.quit();
1017
1221
  } else {
1018
- app.on('second-instance', () => {
1222
+ app.on('second-instance', (_e, argv = []) => {
1019
1223
  if (window) {
1020
1224
  if (window.isMinimized()) window.restore();
1021
1225
  window.focus();
1022
1226
  }
1227
+ const link = argv.find((a) => String(a).startsWith(`${JOIN_SCHEME}://`));
1228
+ if (link) openJoinLink(link);
1023
1229
  });
1230
+ // Stripe's last page links back on the app's scheme, which only needs the window.
1231
+ app.on('open-url', (e, url) => { e.preventDefault(); if (ourLink(url)) showWindow(); openJoinLink(url); });
1024
1232
  app.whenReady().then(() => {
1025
1233
  // A from-source Linux launch keeps the launcher command installed. The
1026
1234
  // entry runs that command; this only refreshes where it points. A packaged
@@ -1036,6 +1244,9 @@ if (!app.requestSingleInstanceLock()) {
1036
1244
  // THE ABOUT BOX SAYS AGENTBOX AND ITS OWN VERSION (w-db6f5e331e). Run from
1037
1245
  // source it read the bundle's, which is Electron's: "Electron 43.0.0".
1038
1246
  app.setAboutPanelOptions({ applicationName: NAME, applicationVersion: app.getVersion(), version: '' });
1247
+ // Only the installed app takes the invite scheme: run from source it would
1248
+ // point every link on the Mac at this checkout's Electron.
1249
+ if (app.isPackaged) app.setAsDefaultProtocolClient(JOIN_SCHEME);
1039
1250
  // Serve a granted file, and nothing else. A page under this scheme is one
1040
1251
  // of ours, but it is written by an agent and read by her, so the check is
1041
1252
  // on every request rather than only on the one the pane asked for.