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/settings.mjs CHANGED
@@ -12,27 +12,34 @@
12
12
  // place a grant is reviewed; what changed is that she edits them as fields on
13
13
  // the project they belong to instead of in a text editor.
14
14
 
15
+ import { teammatesOn } from '../shared/teammates.mjs';
16
+ import { phoneChoiceOf, phoneProjectsOf, withPhoneAll, withPhoneProject, phoneEligible } from '../shared/phone-snapshot.mjs';
15
17
  import fs from 'node:fs';
18
+ import { harnessFor } from './harnesses.mjs';
16
19
  import os from 'node:os';
17
20
  import path from 'node:path';
18
21
  import { codexChoiceToOpen, saveConfig } from './config.mjs';
19
- import { resolveClaudeBin, forgetClaudeBin, INSTALL_URL } from './claude-bin.mjs';
22
+ import { forgetClaudeBin, INSTALL_URL } from './claude-bin.mjs';
23
+ import { lookAgain } from './engine-bin.mjs';
20
24
  import { AGENT_MODES } from '../shared/agents.mjs';
21
25
  import { analyticsKey, diagnosticsOn } from './analytics.mjs';
22
26
  import { CLAUDE_PERMISSION_MODES } from '../shared/work-items.mjs';
23
- import { accountSentence, engineTroubleNote } from '../shared/spawn-trouble.mjs';
27
+ import { accountSentence, engineTroubleNote, refineCause } from '../shared/spawn-trouble.mjs';
24
28
  import { readPlan } from './claude-plan.mjs';
25
- import { effectiveProfiles } from './account-discovery.mjs';
29
+ import { effectiveProfiles, troubleForProfile } from './account-discovery.mjs';
26
30
  import { machineSlots, machineNote, autoAgents, MAX_SLOTS } from './machine.mjs';
27
- import { NAME } from '../shared/product-name.mjs';
31
+ import { shortPath } from './agent-files.mjs';
32
+ import { NAME, Name } from '../shared/product-name.mjs';
28
33
  import { autoSlots } from './memory-gate.mjs';
29
34
  import { accountIdentity, claudeLoginCommand, duplicateAccountNote, linkAccountTooling, makeClaudeHome } from './account-tooling.mjs';
30
35
  import { isEngine } from '../shared/engines.mjs';
31
- import { codexAccount, codexLoginCommand, makeCodexHome } from './codex-account.mjs';
32
- import { codexDefaultModel, codexModels } from './codex-models.mjs';
36
+ import { codexAccount, codexFreePlan, codexLoginCommand, makeCodexHome } from './codex-account.mjs';
37
+ import { codexDefaultModel, codexModelsAcross } from './codex-models.mjs';
33
38
  import { CODEX_DEFAULT_MODE, isCodexMode } from '../shared/codex-modes.mjs';
39
+ import { CLOUD_SAME, CLOUD_CLAUDE_DEFAULT, CLOUD_CODEX_DEFAULT } from './cloud-permission.mjs';
40
+ import { isPackagedElectron } from './team/session.mjs';
34
41
  import { claudeModels } from './claude-models.mjs';
35
- import { resolveCodexBin, forgetCodexBin, INSTALL_URL as CODEX_INSTALL_URL } from './codex-bin.mjs';
42
+ import { forgetCodexBin, INSTALL_URL as CODEX_INSTALL_URL } from './codex-bin.mjs';
36
43
 
37
44
  // Her Claude Code sessions in her inbox: all of them, only the ones stopped on
38
45
  // a question, or none. One reader, here, so the settings screen and the
@@ -213,6 +220,8 @@ export function buildSessionArgs(base, mode) {
213
220
  return [...model, ...(tools.length ? ['--allowedTools', ...tools] : []), ...grant, ...parsed.other];
214
221
  }
215
222
 
223
+ export { cloudPermissionArgs } from './cloud-permission.mjs';
224
+
216
225
  // THE MODEL, WRITTEN BACK INTO THE SAME LIST buildSessionArgs READS FROM.The
217
226
  // screen had no control over this at all; the flag was already there and only
218
227
  // a text editor could change it.
@@ -245,16 +254,42 @@ export function setModelArg(base, model) {
245
254
 
246
255
  const lineCount = (text) => (text.trim() ? text.trim().split('\n').length : 0);
247
256
 
257
+ // What launch found, or the last Check again, in the finder's own shape. For a
258
+ // read nobody is looking at: the search starts a login shell on the window's
259
+ // thread, and the phone reads settings every minute.
260
+ function knownEngine(config, engine) {
261
+ if (engine === 'claude') {
262
+ return {
263
+ path: config.claudeBin ?? null,
264
+ found: !!config.claudeFound,
265
+ certain: config.claudeCertain !== false,
266
+ from: config.claudeBinConfigured ? 'settings' : (config.claudeFrom ?? null),
267
+ };
268
+ }
269
+ // recheckCodex moves codexBin and not codexFound, so the path is the answer.
270
+ return {
271
+ path: config.codexBin ?? null,
272
+ found: !!config.codexBin,
273
+ certain: config.codexCertain !== false,
274
+ from: config.codexBinConfigured ? 'settings' : (config.codexFrom ?? null),
275
+ };
276
+ }
277
+
248
278
  // Claude Code: the path, whether it is really there, and where to get it when
249
279
  // it is not. One reader, here, so the screen and the config cannot disagree.
250
- export function claudeState(config) {
251
- const found = resolveClaudeBin(config.claudeBinConfigured ?? null);
280
+ export function claudeState(config, { search = true } = {}) {
281
+ const found = search ? harnessFor('claude').discover(config.claudeBinConfigured ?? null) : knownEngine(config, 'claude');
252
282
  return {
253
283
  claudeBin: found.path ?? config.claudeBin,
254
284
  claudeFound: found.found,
255
285
  // Whether the search got an answer at all.
256
286
  claudeCertain: found.certain !== false,
257
287
  claudeInstallUrl: INSTALL_URL,
288
+ // WHETHER SOMEBODY POINTED AT THIS FILE THEMSELVES, which is the one thing
289
+ // the card cannot work out from the path alone. A chosen path is never
290
+ // searched around, so the row that offers the search back has to know
291
+ // (`pointAtEngineBin` below).
292
+ claudeChosen: found.from === 'settings',
258
293
  };
259
294
  }
260
295
 
@@ -267,6 +302,20 @@ export function claudeState(config) {
267
302
  // `forgetClaudeBin` exists for.
268
303
  export function recheckClaude(config) {
269
304
  forgetClaudeBin();
305
+ return takeClaude(config);
306
+ }
307
+
308
+ /**
309
+ * Check again without holding the window: the shell is asked in the background
310
+ * and this settles once its answer is on the config. For every caller that can
311
+ * wait (the buttons, setup, the update check).
312
+ */
313
+ export async function lookAgainFor(engine, config) {
314
+ await lookAgain(engine);
315
+ return engine === 'claude' ? takeClaude(config) : takeCodex(config);
316
+ }
317
+
318
+ function takeClaude(config) {
270
319
  const state = claudeState(config);
271
320
  // The config carries the path everything else spawns from, so a search that
272
321
  // has just found Claude Code updates it rather than leaving the rest of the
@@ -275,7 +324,10 @@ export function recheckClaude(config) {
275
324
  // And whether it is here at all, which is what decides that a Mac with only
276
325
  // Codex runs on Codex (`Supervisor#_enginesFound`). Only a certain answer
277
326
  // moves it, so an unsure search never takes Claude Code away from anybody.
278
- if (state.claudeFound || state.claudeCertain) config.claudeFound = state.claudeFound;
327
+ if (state.claudeFound || state.claudeCertain) {
328
+ config.claudeFound = state.claudeFound;
329
+ config.claudeCertain = state.claudeCertain;
330
+ }
279
331
  return state;
280
332
  }
281
333
 
@@ -309,8 +361,8 @@ export function recheckClaude(config) {
309
361
  * checked by uninstalling Codex from the machine running the suite is a card
310
362
  * whose unsure state is not checked. Nothing in the app passes it.
311
363
  */
312
- export function codexState(config, where = {}) {
313
- const found = resolveCodexBin(config.codexBinConfigured ?? null, where);
364
+ export function codexState(config, where = {}, { search = true } = {}) {
365
+ const found = search ? harnessFor('codex').discover(config.codexBinConfigured ?? null, where) : knownEngine(config, 'codex');
314
366
  return {
315
367
  // Empty rather than a path when there is nothing there, which is the same
316
368
  // asymmetry `config.codexBin` already keeps (main/config.mjs): a configured
@@ -322,12 +374,14 @@ export function codexState(config, where = {}) {
322
374
  // Whether the search got an answer at all.
323
375
  certain: found.certain !== false,
324
376
  url: CODEX_INSTALL_URL,
377
+ // Whether somebody pointed at this file themselves. See `claudeState`.
378
+ chosen: found.from === 'settings',
325
379
  };
326
380
  }
327
381
 
328
382
  /** `codexState` under the names the walk's last card reads beside Claude's. */
329
- export function codexFoundState(config, where = {}) {
330
- const state = codexState(config, where);
383
+ export function codexFoundState(config, where = {}, how = {}) {
384
+ const state = codexState(config, where, how);
331
385
  return { codexFound: state.found, codexCertain: state.certain, codexInstallUrl: state.url };
332
386
  }
333
387
 
@@ -339,8 +393,13 @@ export function codexFoundState(config, where = {}) {
339
393
  * without a restart. */
340
394
  export function recheckCodex(config) {
341
395
  forgetCodexBin();
396
+ return takeCodex(config);
397
+ }
398
+
399
+ function takeCodex(config) {
342
400
  const state = codexState(config);
343
401
  config.codexBin = state.found ? state.bin : null;
402
+ config.codexCertain = state.certain;
344
403
  // Found while the app is open, by this button or the plan setup: the choice
345
404
  // opens now, as it would have at the next launch (main/config.mjs).
346
405
  const opened = codexChoiceToOpen(config, state.found);
@@ -348,7 +407,100 @@ export function recheckCodex(config) {
348
407
  return state;
349
408
  }
350
409
 
351
- export function readSettings({ config, supervisor, store }) {
410
+ /* ------------------- POINTING AT IT YOURSELF, AND BACK -------------------- */
411
+ /**
412
+ * THE WAY OUT OF A SEARCH THAT MISSED, AND IT IS NOT A RELEASE (w-ce8c1a9275).
413
+ *
414
+ * The note this came from: "It has a really hard time finding the install on the
415
+ * person's computer, and we keep having bug reports and then patching that
416
+ * specific case." Asking the login shell first (main/engine-bin.mjs) covers
417
+ * every installer a person's own PATH knows about, which is most of them. It
418
+ * cannot cover a wrapper script somewhere nobody would guess, a tool on a
419
+ * volume, or a machine whose login shell will not start at all -- and for those,
420
+ * every fix used to be a path WE ship and a release THEY wait for.
421
+ *
422
+ * `claudeBin` in zero.config.json already won over the search and always has.
423
+ * What was missing is that nobody is told about that file and the app gave no
424
+ * way to edit it. These two functions are that door, and the second one matters
425
+ * as much as the first: a wrong choice that could only be undone by hand-editing
426
+ * a file nobody has heard of is not a door, it is a trap.
427
+ *
428
+ * THE REFUSALS ARE MOST OF THE WORK. Each thing refused here was possible by
429
+ * hand before and each one becomes a spawn that fails once per row, naming a
430
+ * file nobody meant to name. The sentence comes back to the screen instead.
431
+ *
432
+ * NO PATH FROM THE WINDOW IS EVER TRUSTED: main/ipc.mjs opens the picker and
433
+ * calls this in the same trip, so the only path that reaches here is one the
434
+ * open panel itself produced. The sibling rule is
435
+ * tests/where-codex-runs-from-is-not-a-file-a-worker-can-write.test.mjs.
436
+ */
437
+ const BIN_KEY = { claude: 'claudeBin', codex: 'codexBin' };
438
+
439
+ /** The sentence, or null when the file really is one we could run. */
440
+ function whyNotAnAgent(chosen) {
441
+ if (typeof chosen !== 'string' || !path.isAbsolute(chosen)) {
442
+ return `${Name} needs the file itself, picked from your computer.`;
443
+ }
444
+ let stat;
445
+ try { stat = fs.statSync(chosen); } catch { stat = null; }
446
+ if (!stat) return `${Name} could not find anything there. It may have moved since.`;
447
+ if (stat.isDirectory()) return 'That is a folder. Open it and pick the file inside.';
448
+ try { fs.accessSync(chosen, fs.constants.X_OK); } catch {
449
+ return 'That file cannot be run, so it is not the one.';
450
+ }
451
+ return null;
452
+ }
453
+
454
+ /**
455
+ * Take a file somebody picked for one coding agent.
456
+ *
457
+ * Written to zero.config.json so it survives a restart AND set on the live
458
+ * config, because every consumer reads `config.*` per call and a setting that
459
+ * waits for a relaunch is the same failure as a fix that is not running. The
460
+ * recheck afterwards is what puts the path the fleet spawns from in step with
461
+ * it, the same way the Check again button does.
462
+ */
463
+ export function pointAtEngineBin(config, engine, chosen) {
464
+ const key = BIN_KEY[engine];
465
+ if (!key) return { ok: false, say: `${Name} does not know a coding agent called that.` };
466
+ const why = whyNotAnAgent(chosen);
467
+ if (why) return { ok: false, say: why };
468
+ if (!config.appDir) return { ok: false, say: `${Name} has nowhere to save that.` };
469
+ saveConfig(config, { [key]: chosen });
470
+ config[`${engine}BinConfigured`] = chosen;
471
+ const state = engine === 'claude' ? recheckClaude(config) : recheckCodex(config);
472
+ return { ok: true, state };
473
+ }
474
+
475
+ /**
476
+ * Give the choice back, so the search is in charge again. The key is REMOVED
477
+ * from the file rather than emptied: `saveConfig` reads an explicit undefined as
478
+ * a deletion, and an empty string left behind would read as a setting that means
479
+ * something else (main/config.mjs only treats a non-empty string as chosen).
480
+ */
481
+ export function searchForEngineBin(config, engine) {
482
+ const key = BIN_KEY[engine];
483
+ if (!key) return { ok: false, say: `${Name} does not know a coding agent called that.` };
484
+ if (!config.appDir) return { ok: false, say: `${Name} has nowhere to save that.` };
485
+ saveConfig(config, { [key]: undefined });
486
+ config[`${engine}BinConfigured`] = null;
487
+ const state = engine === 'claude' ? recheckClaude(config) : recheckCodex(config);
488
+ return { ok: true, state };
489
+ }
490
+
491
+ // THE ALPHA FEATURES SWITCHES (w-3d0fa23ffb). Off unless someone turned them
492
+ // on in Settings; only a real `true` counts. Multiplayer, the team version, is
493
+ // the exception in a checkout, where it was always on.
494
+ export function alphaFeatures(config, { packaged = isPackagedElectron() } = {}) {
495
+ return {
496
+ rooms: config?.alphaRooms === true,
497
+ teammates: teammatesOn(config),
498
+ multiplayer: packaged ? config?.alphaMultiplayer === true : config?.alphaMultiplayer !== false,
499
+ };
500
+ }
501
+
502
+ // `search: false` is for a read on a timer (`knownEngine`).
503
+ export function readSettings({ config, supervisor, store }, { search = true } = {}) {
352
504
  const products = store.listProducts();
353
505
  const status = supervisor.status();
354
506
  const sessions = [...supervisor.sessions.values()];
@@ -379,21 +531,36 @@ export function readSettings({ config, supervisor, store }) {
379
531
  // waiting, because a page she chose to open interrupts nobody.
380
532
  const troubles = status.accountTrouble ?? {};
381
533
  const accounts = (profiles.length ? profiles : ['default']).map((profile) => {
382
- const trouble = troubles[profile] ?? null;
534
+ // THE HOME THE WORKSPACE IS CONFIGURED WITH, not this process's own. Both
535
+ // of these read a login out of a folder, and both of them defaulted to
536
+ // `os.homedir()` while the list of folders above came from `config.home`,
537
+ // so a workspace pointed anywhere else drew rows for its own folders and
538
+ // read somebody else's login into them.
539
+ const plan = readPlan(profile, { home: config.home });
540
+ // A reason filed before an ended subscription had its own name still reads
541
+ // as an admin's block; the login's file says which it is.
542
+ // THE SUBSCRIPTION'S TROUBLE, not the folder's: one row stands for every
543
+ // folder holding this account (account-discovery.mjs says why).
544
+ const filed = troubleForProfile(troubles, profile, { home: config.home });
545
+ const trouble = filed ? { ...filed, cause: refineCause(filed.cause, plan) } : null;
383
546
  const resting = (supervisor._profileCooldown?.[profile] ?? 0) >= now;
384
547
  // WHOSE SUBSCRIPTION THIS ROW IS, not merely which folder it lives in.
385
548
  // The page named its rows after folders, so nothing on it could tell a
386
549
  // person that one of their accounts had been replaced by the other;
387
550
  // main/account-tooling.mjs has how that happens. Null when the
388
551
  // folder holds no login, and then the row reads as it always did.
389
- const identity = accountIdentity(profile === 'default' ? null : profile);
552
+ const identity = accountIdentity(profile === 'default' ? null : profile, { home: config.home });
390
553
  return {
391
554
  profile,
392
555
  email: identity?.email ?? null,
393
556
  accountUuid: identity?.accountUuid ?? null,
394
- // WHICH ONE RUNS, when she has picked one. False on every row while she
395
- // has not, and then every account runs as it always has.
396
- chosen: (config.activeAccount?.claude ?? null) === profile,
557
+ // WHICH ONE RUNS, AND ONE OF THEM ALWAYS DOES (w-dac993a554). This was
558
+ // false on every row until somebody picked, because until then every
559
+ // account really did take a share of the work. One account runs it now,
560
+ // so the supervisor is asked which rather than the config file: with
561
+ // nothing picked that is the primary login, and the page says so instead
562
+ // of leaving a person to guess which of their two plans is being spent.
563
+ chosen: (supervisor._profilesFor?.('claude') ?? [])[0] === profile,
397
564
  // The default profile is whatever ~/.claude holds; anything else is a
398
565
  // second logged-in home directory, and its path is the only name it has.
399
566
  label: profile === 'default' ? 'default' : path.basename(profile),
@@ -421,10 +588,12 @@ export function readSettings({ config, supervisor, store }) {
421
588
  // Read here rather than kept, because Claude Code refreshes its own
422
589
  // profile as it runs and this page is opened by hand a few times a day.
423
590
  // `null` when the file cannot be read, and then no screen says anything.
424
- plan: (() => { const p = readPlan(profile); return p.known ? { label: p.label, max: p.max } : null; })(),
591
+ plan: plan.known ? { label: plan.label, max: plan.max } : null,
425
592
  };
426
593
  });
427
594
 
595
+ // On her phone: every project unless she kept it here (shared/phone-snapshot.mjs).
596
+ const phoneSlugs = new Set(phoneProjectsOf(config.phoneProducts, process.env.AGENTBOX_PERSON_ID, phoneEligible(products)));
428
597
  const projects = products.map((product) => {
429
598
  let instructions = '';
430
599
  try { instructions = supervisor.readProjectInstructions(product.slug); } catch {}
@@ -446,6 +615,9 @@ export function readSettings({ config, supervisor, store }) {
446
615
  // workspace default and never a strip of Claude Code jargon on a page
447
616
  // she opens to change her rules.
448
617
  autonomous: (config.autonomousProducts ?? []).includes(product.slug),
618
+ // ON HER PHONE WITH THE MAC SHUT (docs/cloud/PHONE-PLAN.md): its threads
619
+ // go up to her own rows online on every sync (main/phone-inbox.mjs).
620
+ onPhone: phoneSlugs.has(product.slug),
449
621
  permission: override ? permissionMode(override) : 'workspace',
450
622
  permissionArgs: override ?? null,
451
623
  codexMode: config.projectCodexMode?.[product.slug] ?? 'workspace',
@@ -470,8 +642,12 @@ export function readSettings({ config, supervisor, store }) {
470
642
  return {
471
643
  archivedProjects,
472
644
  workspace: {
645
+ // THE PHONE AS A WHOLE: on unless she turned it off (shared/phone-snapshot.mjs).
646
+ phoneAll: phoneChoiceOf(config.phoneProducts, process.env.AGENTBOX_PERSON_ID, phoneEligible(products)).all,
473
647
  agentsRunning: !supervisor.paused,
474
- sessionsAtOnce: config.maxConcurrentSessions,
648
+ // The number picked on the menu, for the whole computer, or what is
649
+ // running as the limit now when nothing was picked.
650
+ sessionsAtOnce: typeof config.agentsAtOnce === 'number' ? config.agentsAtOnce : status.capacity,
475
651
  // AUTOMATIC, AND WHAT IT WORKS OUT TO (w-e5225b62ba). `agentsAuto` is who
476
652
  // is deciding; `agentsTotal` is what the whole Mac is running at once,
477
653
  // which is the number the page shows, because the per-account one is
@@ -499,6 +675,7 @@ export function readSettings({ config, supervisor, store }) {
499
675
  machineNote: machineNote(slotsHere, config.maxConcurrentSessions),
500
676
  memoryGate: memoryGateSettings({ config, supervisor }),
501
677
  leftovers: leftoverSettings({ config, supervisor }),
678
+ strayFolders: strayFolderSettings({ supervisor }),
502
679
  capacity: status.capacity,
503
680
  running: sessions.length,
504
681
  model: parseSessionArgs(workspaceArgs).model,
@@ -511,9 +688,11 @@ export function readSettings({ config, supervisor, store }) {
511
688
  // comes back with ONE entry and this screen draws exactly what it drew
512
689
  // before. A page that derived its own list would offer her a choice the
513
690
  // supervisor is about to refuse.
514
- ...engineSettings(supervisor, config),
691
+ ...engineSettings(supervisor, config, { search }),
515
692
  permission: permissionMode(workspaceArgs),
516
693
  permissionArgs: workspaceArgs,
694
+ cloudPermission: config.cloudPermission ?? CLOUD_CLAUDE_DEFAULT,
695
+ cloudCodexMode: config.cloudCodexMode ?? CLOUD_CODEX_DEFAULT,
517
696
  // ONE SWITCH FOR EVERYTHING THAT LEAVES THE MACHINE, and it is on when
518
697
  // Agentbox is installed (legal/privacy.html, section 10). Off stops the
519
698
  // counts AND the crash reports; there is deliberately no partial mode and
@@ -526,6 +705,11 @@ export function readSettings({ config, supervisor, store }) {
526
705
  // ADHD mode: whether the ADHD rules ride under hers (w-5737fe67cf). Off
527
706
  // unless she turned it on, so only `true` means on.
528
707
  adhdMode: config.adhdMode === true,
708
+ importNew: config.importNew === true,
709
+ alpha: alphaFeatures(config),
710
+ // The corner tag over other apps (w-dafae58a23). On unless she turned it
711
+ // off here, which is the only place it turns off for good.
712
+ cornerTag: config.cornerTag !== false,
529
713
  // How much of her own Claude Code goes in her inbox: all / waiting / off.
530
714
  // All by default, because the machine nobody has opened this page on is
531
715
  // the one with thirteen forgotten sessions on it.
@@ -550,11 +734,11 @@ export function readSettings({ config, supervisor, store }) {
550
734
  // Looked up again on every read of this screen, not taken from boot.
551
735
  // Someone who reads "install it, then open this screen again" and does
552
736
  // exactly that has to see it turn found, without restarting the app.
553
- ...claudeState(config),
737
+ ...claudeState(config, { search }),
554
738
  // AND WHETHER CODEX IS, because the app needs one of the two and not
555
739
  // both: the last card of the walk only shuts the inbox when neither is
556
740
  // here (renderer/src/App.tsx).
557
- ...codexFoundState(config),
741
+ ...codexFoundState(config, {}, { search }),
558
742
  // AND WHAT THIS MAC'S CLAUDE CODE CALLS ITS MODELS.
559
743
  //
560
744
  // In the same trip, and for the same reason the line above is looked up
@@ -606,7 +790,7 @@ function engineTroubleFor(supervisor, engine) {
606
790
  return trouble ? engineTroubleNote(trouble.cause) : null;
607
791
  }
608
792
 
609
- function engineSettings(supervisor, config) {
793
+ function engineSettings(supervisor, config, { search = true } = {}) {
610
794
  const choices = supervisor.engineChoices();
611
795
  const engine = supervisor.engineFacts().workspace;
612
796
  // WHETHER THIS MAC HAS ASKED TO HEAR ABOUT CODEX AT ALL, which is a different
@@ -631,7 +815,7 @@ function engineSettings(supervisor, config) {
631
815
  // links the install, which is the question that person had.
632
816
  const codexIsHome = engine === 'codex';
633
817
  const codex = {
634
- ...codexState(config),
818
+ ...codexState(config, {}, { search }),
635
819
  trouble: engineTroubleFor(supervisor, 'codex'),
636
820
  account: codexAccount(supervisor._codexHome()),
637
821
  // EVERY CODEX LOGIN ON THE MAC, NOT JUST THE FIRST. The supervisor has
@@ -646,6 +830,7 @@ function engineSettings(supervisor, config) {
646
830
  return { engine, engineChoices: choices, codex, codexModels: [], codexModelDefault: null, codexModel: null };
647
831
  }
648
832
  const home = supervisor._codexHome();
833
+ const offered = supervisor._codexOfferHomes();
649
834
  return {
650
835
  engine,
651
836
  engineChoices: choices,
@@ -654,9 +839,12 @@ function engineSettings(supervisor, config) {
654
839
  // strip in the model drawer draws the picked model's list, so they travel
655
840
  // beside the name rather than as a list of their own that could disagree
656
841
  // with it.
657
- codexModels: codexModels({ home }).map((m) => ({
842
+ codexModels: codexModelsAcross(offered).map((m) => ({
658
843
  id: m.id, label: m.label, levels: m.levels.map((l) => l.id), defaultLevel: m.defaultLevel,
659
844
  })),
845
+ // Set when every login offered is on ChatGPT's free plan, so the menu can
846
+ // say why Codex lists so few models.
847
+ codexFreePlan: codexFreePlan(offered),
660
848
  // What her own `codex` in a terminal runs, so the card's word and her
661
849
  // terminal cannot disagree. Null is a real answer and the card says
662
850
  // "Codex's own" for it rather than guessing (renderer/src/models.ts).
@@ -686,12 +874,15 @@ const withSlug = (list, slug, on) => {
686
874
  // `paused` and `driven` were the other three and all three are gone from the
687
875
  // app: the first two because she could not tell what they did and would never
688
876
  // have used them, the third because the self-driving it belonged to is deleted.
689
- export function setProjectSetting({ config, supervisor }, { product, key, value }) {
877
+ export function setProjectSetting({ config, supervisor, store }, { product, key, value }) {
690
878
  if (!product) throw new Error('no project');
691
879
  switch (key) {
692
880
  case 'autonomous':
693
881
  saveConfig(config, { autonomousProducts: withSlug(config.autonomousProducts, product, !!value) });
694
882
  break;
883
+ case 'phone':
884
+ saveConfig(config, { phoneProducts: withPhoneProject(config.phoneProducts, process.env.AGENTBOX_PERSON_ID, product, !!value, phoneEligible(store?.listProducts?.() ?? [])) });
885
+ break;
695
886
  // WHICH OF HER CLAUDE CODE AGENTS THIS PROJECT TOOK. Names only: the files
696
887
  // stay where their author put them and Agentbox never copies or moves one,
697
888
  // so a list here that has gone stale costs nothing and an agent she
@@ -769,7 +960,9 @@ export function setProjectSetting({ config, supervisor }, { product, key, value
769
960
  * not signed in.
770
961
  */
771
962
  function codexAccountRows(supervisor) {
772
- const chosen = supervisor.config.activeAccount?.codex ?? null;
963
+ // THE ONE THAT RUNS, the same way the Claude rows answer it: the supervisor's
964
+ // own pick, which is the chosen account or the primary login (w-dac993a554).
965
+ const chosen = supervisor._profilesFor?.('codex')?.[0] ?? null;
773
966
  return supervisor._codexProfiles().map((profile) => {
774
967
  const home = supervisor._codexProfileHome(profile);
775
968
  const id = codexAccount(home);
@@ -781,8 +974,7 @@ function codexAccountRows(supervisor) {
781
974
  planNamed: id?.planNamed ?? false,
782
975
  accountId: id?.accountId ?? null,
783
976
  signedIn: !!id,
784
- // WHICH ONE RUNS. Null when she has never picked, and then every account
785
- // runs and the card says so rather than marking one at random.
977
+ // WHICH ONE RUNS, and one of them always does (`chosen` above).
786
978
  chosen: chosen === profile,
787
979
  };
788
980
  });
@@ -827,9 +1019,12 @@ export function addCodexAccount({ config, supervisor }) {
827
1019
  * ordinary reason for wanting it: a work login and a personal login, both
828
1020
  * live, on one Mac.
829
1021
  *
830
- * Everything else is the Codex flow exactly. Main makes the folder and
831
- * registers the profile, so the row appears immediately reading "Not signed
832
- * in"; the terminal is left with the browser half, which is genuinely hers.
1022
+ * Main makes the folder and the terminal is left with the browser half, which
1023
+ * is genuinely hers. UNLIKE CODEX, NOTHING IS WRITTEN INTO THE CONFIG: the
1024
+ * folder is the shape main/account-discovery.mjs finds, so the account appears
1025
+ * the moment its sign-in lands and never before. Registering it first put an
1026
+ * abandoned sign-in in Settings as a "Not signed in" account and handed it
1027
+ * every other task (tests/an-account-shows-once-and-only-once-it-is-signed-in).
833
1028
  *
834
1029
  * THE TOOLING IS LINKED AT CREATION, which the Codex side has no equivalent of.
835
1030
  * `linkAccountTooling` otherwise runs at the first spawn, and the first thing
@@ -839,9 +1034,6 @@ export function addCodexAccount({ config, supervisor }) {
839
1034
  */
840
1035
  export function addClaudeAccount({ config, supervisor }) {
841
1036
  const made = makeClaudeHome({ home: config.home });
842
- const existing = (config.authProfiles ?? []).map((x) => x || 'default');
843
- const list = existing.length ? existing : ['default'];
844
- saveConfig(config, { authProfiles: [...list, made.profile] });
845
1037
  // A failure here is a folder without her skills in it, which is a smaller
846
1038
  // thing than a sign in that does not happen: the links are retried at every
847
1039
  // spawn anyway.
@@ -891,6 +1083,9 @@ export function memoryGateSettings({ config, supervisor }) {
891
1083
  export function memoryReading({ config, supervisor }) {
892
1084
  const out = { heavy: null, waiting: null, pressure: null, usedPct: null, asked: null, waited: null, refused: null };
893
1085
  if (!config.memoryGate) return out;
1086
+ // The supervisor's own hold on new agents (main/start-hold.mjs), which does
1087
+ // not depend on which copy of the app is coordinating the command queue.
1088
+ try { out.holding = !!supervisor.startHoldStatus?.()?.holding; } catch { out.holding = false; }
894
1089
  let s = null;
895
1090
  try { s = supervisor.memoryGateStatus?.() ?? null; } catch { return out; }
896
1091
  if (!s || s.role === 'standby') return out;
@@ -923,22 +1118,26 @@ export function leftoverSettings({ config, supervisor }) {
923
1118
  // Codex, 2026-10-05).
924
1119
  if (!on) {
925
1120
  if (total && stoppable) {
926
- now = `Right now finished agents have left ${total} program${total === 1 ? '' : 's'} running; turning this on stops ${stoppable === total ? (total === 1 ? 'it' : 'them') : `${stoppable} of them`}, two hours from then.`;
1121
+ now = `Right now finished agents have left ${total} program${total === 1 ? '' : 's'} running; turning this on stops ${stoppable === total ? (total === 1 ? 'it' : 'them') : `${stoppable} of them`} when you close their tasks.`;
927
1122
  }
928
1123
  return { on, now };
929
1124
  }
930
1125
  {
931
1126
  if (!total) now = 'Nothing left running by finished agents.';
932
1127
  else {
933
- const next = list.filter((l) => l.programs && l.stopsAt).map((l) => l.stopsAt).sort((a, b) => a - b)[0];
1128
+ const timed = list.filter((l) => l.programs && l.stopsAt);
1129
+ const next = timed.map((l) => l.stopsAt).sort((a, b) => a - b)[0];
1130
+ const soon = timed.reduce((n, l) => n + l.programs, 0);
1131
+ const open = stoppable - soon;
934
1132
  const when = (ms) => {
935
1133
  const m = Math.max(1, Math.round((ms - Date.now()) / 60_000));
936
1134
  return m >= 60 ? `${Math.floor(m / 60)} h${m % 60 ? ` ${m % 60} m` : ''}` : `${m} m`;
937
1135
  };
938
- now = `Finished agents have left ${total} program${total === 1 ? '' : 's'} running.`;
939
- if (stoppable) now += ` ${stoppable} ${next && next > Date.now() ? `stop in ${when(next)}` : 'are stopping'}`;
940
- if (kept) now += `${stoppable ? ';' : ''} ${kept} ${kept === 1 ? 'is' : 'are'} kept`;
941
- now += '.';
1136
+ const parts = [];
1137
+ if (soon) parts.push(`${soon} ${next > Date.now() ? `stop${soon === 1 ? 's' : ''} in ${when(next)}` : `${soon === 1 ? 'is' : 'are'} stopping`}`);
1138
+ if (open) parts.push(`${open} stop${open === 1 ? 's' : ''} when you close ${open === 1 ? 'its task' : 'their tasks'}`);
1139
+ if (kept) parts.push(`${kept} ${kept === 1 ? 'is' : 'are'} kept`);
1140
+ now = `Finished agents have left ${total} program${total === 1 ? '' : 's'} running. ${parts.join('; ')}.`;
942
1141
  const survivors = s?.survivors?.length ?? 0;
943
1142
  if (survivors) now += ` ${survivors} did not stop when asked.`;
944
1143
  }
@@ -946,8 +1145,59 @@ export function leftoverSettings({ config, supervisor }) {
946
1145
  return { on, now };
947
1146
  }
948
1147
 
949
- export function setWorkspaceSetting({ config, supervisor }, { key, value }) {
1148
+ /**
1149
+ * COPIES OF A PROJECT THE APP DID NOT MAKE, SAID OUT LOUD (w-330eea6c66).
1150
+ *
1151
+ * MEASURED 2026-10-07: thirteen worktrees of one repository sat outside the
1152
+ * folder the app keeps its own in, about 740 MB, and nothing in the app could
1153
+ * list, show or sweep one. That is the same invisibility that stranded 26 GB on
1154
+ * 2026-09-22, and the half that fixes it for a person is being told.
1155
+ *
1156
+ * NO BUTTON, DELIBERATELY. A folder the app did not make is not the app's to
1157
+ * remove: it is the only copy of whatever is uncommitted inside it, and somebody
1158
+ * made it on purpose. So this is a sentence and nothing else, and it is absent
1159
+ * entirely when there is nothing to say, because a row that is usually quiet
1160
+ * trains the reaction it exists to prevent.
1161
+ */
1162
+ export function strayFolderSettings({ supervisor }, home = os.homedir()) {
1163
+ let list = [];
1164
+ try { list = supervisor?.strayFolders?.() ?? []; } catch { list = []; }
1165
+ if (!list.length) return null;
1166
+ const count = list.length;
1167
+ const one = count === 1;
1168
+ const projects = [...new Set(list.map((w) => w.project).filter(Boolean))];
1169
+ const named = list.slice(0, 3).map((w) => shortPath(w.path, home));
1170
+ const rest = count - named.length;
1171
+ const of = projects.length ? ` of ${projects.slice(0, 2).join(' and ')}${projects.length > 2 ? ' and other projects' : ''}` : '';
1172
+ const now = `${count} ${one ? 'copy' : 'copies'}${of} ${one ? 'was' : 'were'} made by hand outside the folder ${NAME} keeps its own in, and ${one ? 'is' : 'are'} holding disk until you remove ${one ? 'it' : 'them'}: ${named.join(', ')}${rest > 0 ? ` and ${rest} more` : ''}. ${NAME} never removes a folder it did not make.`;
1173
+ return { count, now };
1174
+ }
1175
+
1176
+ export function cornerTagTurnedOff({ key, value } = {}) {
1177
+ return key === 'cornerTag' && !value;
1178
+ }
1179
+
1180
+ export function countsTurnedOff({ key, value } = {}) {
1181
+ return key === 'diagnostics' && !value;
1182
+ }
1183
+
1184
+ export function setWorkspaceSetting({ config, supervisor, store }, { key, value }) {
950
1185
  switch (key) {
1186
+ // EVERY PROJECT ON HER PHONE, or none (shared/phone-snapshot.mjs). Which
1187
+ // ones she keeps here is remembered either way.
1188
+ case 'phoneAll':
1189
+ saveConfig(config, { phoneProducts: withPhoneAll(config.phoneProducts, process.env.AGENTBOX_PERSON_ID, !!value, phoneEligible(store?.listProducts?.() ?? [])) });
1190
+ break;
1191
+ // THE DROPDOWN'S WHOLE ANSWER AT ONCE: on or off, and which projects stay
1192
+ // on this computer. Only project slugs that exist are kept.
1193
+ case 'phoneProjects': {
1194
+ const person = process.env.AGENTBOX_PERSON_ID;
1195
+ if (!person) throw new Error('sign in first');
1196
+ const every = phoneEligible(store?.listProducts?.() ?? []);
1197
+ const except = Array.isArray(value?.except) ? value.except.filter((s) => every.includes(s)) : [];
1198
+ saveConfig(config, { phoneProducts: { owner: person, all: value?.all !== false, except } });
1199
+ break;
1200
+ }
951
1201
  case 'agentsRunning':
952
1202
  supervisor.paused = !value;
953
1203
  supervisor.onChange?.();
@@ -979,7 +1229,9 @@ export function setWorkspaceSetting({ config, supervisor }, { key, value }) {
979
1229
  const n = Math.max(1, Math.min(MAX_SLOTS, Math.round(Number(value) || 1)));
980
1230
  // Setting a number by hand IS taking the decision back, so the page never
981
1231
  // shows a stepper that the supervisor is quietly ignoring.
982
- saveConfig(config, { maxConcurrentSessions: n, agentsAuto: false });
1232
+ // FOR THE WHOLE COMPUTER (w-ec27ff1a51), under its own key, so a number
1233
+ // written per login before 5 Oct can never be read as this choice.
1234
+ saveConfig(config, { agentsAtOnce: n, agentsAuto: false });
983
1235
  // An explicit choice replaces the startup plan suggestion for BOTH
984
1236
  // engines. Leaving this marker set makes Codex ignore the new number
985
1237
  // until loadConfig runs again at restart. It is derived, not persisted.
@@ -1006,6 +1258,43 @@ export function setWorkspaceSetting({ config, supervisor }, { key, value }) {
1006
1258
  break;
1007
1259
  // STOP WHAT FINISHED AGENTS LEAVE RUNNING (main/leftovers.mjs). Turning it
1008
1260
  // on records the moment, which is the earliest the clock may start from.
1261
+ // WHERE THREADS RUN (renderer/src/cloud-menu.ts): 'mac' turns cloud runs
1262
+ // off, 'full' sends what does not fit here, 'all' sends everything first.
1263
+ // Read by the supervisor per call, so it takes effect on the next tick.
1264
+ case 'cloudRuns': {
1265
+ const map = { mac: false, full: null, all: 'all' };
1266
+ if (!(value in map)) throw new Error(`unknown cloud mode: ${value}`);
1267
+ // Turning the cloud on to one meaning makes that the one C goes back to.
1268
+ const patch = value === 'mac' ? { cloudRuns: false } : { cloudRuns: map[value], cloudWhenOn: value };
1269
+ saveConfig(config, patch);
1270
+ Object.assign(config, patch);
1271
+ supervisor.wake?.();
1272
+ supervisor.onChange?.();
1273
+ break;
1274
+ }
1275
+ // WHAT "ON" MEANS (renderer/src/cloud-menu.ts): kept while the cloud is
1276
+ // off, and taking effect at once while it is on.
1277
+ case 'cloudWhenOn': {
1278
+ if (value !== 'full' && value !== 'all') throw new Error(`unknown meaning of on: ${value}`);
1279
+ const patch = config.cloudRuns === false ? { cloudWhenOn: value } : { cloudWhenOn: value, cloudRuns: value === 'all' ? 'all' : null };
1280
+ saveConfig(config, patch);
1281
+ Object.assign(config, patch);
1282
+ supervisor.wake?.();
1283
+ supervisor.onChange?.();
1284
+ break;
1285
+ }
1286
+ // THE CLOUD'S OWN MODE PER ENGINE, CLOUD_SAME for this computer's, or null
1287
+ // for the cloud's default (main/cloud-permission.mjs).
1288
+ case 'cloudPermission':
1289
+ case 'cloudCodexMode': {
1290
+ const known = key === 'cloudPermission' ? CLAUDE_MODES.includes(value) : isCodexMode(value);
1291
+ if (value != null && value !== CLOUD_SAME && !known) throw new Error(`unknown permission mode: ${value}`);
1292
+ const patch = { [key]: value ?? null };
1293
+ saveConfig(config, patch);
1294
+ Object.assign(config, patch);
1295
+ supervisor.onChange?.();
1296
+ break;
1297
+ }
1009
1298
  case 'cleanupLeftovers':
1010
1299
  saveConfig(config, value ? { cleanupLeftovers: true, cleanupLeftoversSince: Date.now() } : { cleanupLeftovers: false });
1011
1300
  supervisor.setLeftoverCleanup?.(!!value)?.catch?.((e) => console.warn('zero: leftovers:', e.message));
@@ -1029,6 +1318,31 @@ export function setWorkspaceSetting({ config, supervisor }, { key, value }) {
1029
1318
  case 'adhdMode':
1030
1319
  saveConfig(config, { adhdMode: !!value });
1031
1320
  break;
1321
+ // New chats from Claude Code and Codex come in on their own
1322
+ // (main/new-chats-watch.mjs), only those started after it went on.
1323
+ case 'importNew':
1324
+ if (!value) saveConfig(config, { importNew: false });
1325
+ else if (config.importNew !== true) saveConfig(config, { importNew: true, importNewSince: Date.now() });
1326
+ break;
1327
+ // Rooms is read on every snapshot.
1328
+ case 'alphaRooms':
1329
+ saveConfig(config, { alphaRooms: !!value });
1330
+ supervisor.onChange?.();
1331
+ break;
1332
+ case 'alphaTeammates':
1333
+ saveConfig(config, { alphaTeammates: !!value });
1334
+ supervisor.onChange?.();
1335
+ supervisor.wake?.();
1336
+ break;
1337
+ case 'alphaMultiplayer':
1338
+ saveConfig(config, { alphaMultiplayer: !!value });
1339
+ supervisor.onChange?.();
1340
+ break;
1341
+ // THE CORNER TAG (w-dafae58a23). Turning it back on also ends any hide, so
1342
+ // the switch does what it says the moment she flips it.
1343
+ case 'cornerTag':
1344
+ saveConfig(config, value ? { cornerTag: true, cornerTagHiddenUntil: 0 } : { cornerTag: false });
1345
+ break;
1032
1346
  // WHICH ACCOUNT HER WORK RUNS ON.
1033
1347
  //
1034
1348
  // ONE KEY HOLDS BOTH ENGINES, because a profile word means different things
@@ -1037,15 +1351,15 @@ export function setWorkspaceSetting({ config, supervisor }, { key, value }) {
1037
1351
  // in one object keyed by engine is what stops a Codex pick being read as a
1038
1352
  // Claude one, which is the collision `_accountKey` exists to prevent.
1039
1353
  //
1040
- // NULL CLEARS IT, and clearing means every account runs again, which is what
1041
- // this machine did before she ever picked. `Supervisor#_narrowToChosen` also
1042
- // ignores a name that no longer exists, so a login she removes cannot wedge
1043
- // the fleet.
1354
+ // NULL CLEARS IT, and clearing means the primary login runs the work again.
1355
+ // `Supervisor#_theOneAccount` also ignores a name that no longer exists, so
1356
+ // a login she removes cannot wedge the fleet.
1044
1357
  case 'activeAccount': {
1045
1358
  const engineId = value?.engine;
1046
1359
  if (!isEngine(engineId)) throw new Error(`unknown coding agent: ${engineId}`);
1047
1360
  const profile = typeof value?.profile === 'string' && value.profile ? value.profile : null;
1048
1361
  saveConfig(config, { activeAccount: { ...(config.activeAccount ?? {}), [engineId]: profile } });
1362
+ supervisor.accountChosen?.(engineId, profile);
1049
1363
  supervisor.onChange?.();
1050
1364
  break;
1051
1365
  }