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
@@ -0,0 +1,165 @@
1
+ // THE RELAY'S LOCKS (relay/worker.mjs, main/relay-client.mjs, relay/phone/).
2
+ //
3
+ // A phone away from home reaches the Mac through a relay we run. Everything
4
+ // that crosses it is sealed on one end and opened on the other with keys made
5
+ // from the phone's key, which is in the QR code and nowhere else: it never
6
+ // leaves the Mac except into the code, and the phone keeps it in the address's
7
+ // fragment, which a browser never sends. So the relay can pass messages and
8
+ // cannot read, alter or forge one.
9
+ //
10
+ // What the relay does see: a room name, and the Mac's proof that it may hold
11
+ // that room. The room is a hash of the proof, so the relay can check a Mac
12
+ // against it, and neither one tells it the key.
13
+ //
14
+ // Plain WebCrypto, so this one file runs on the Mac (Node), on Cloudflare, and
15
+ // in the phone's service worker.
16
+
17
+ const enc = new TextEncoder();
18
+ const subtle = () => globalThis.crypto.subtle;
19
+ const SALT = enc.encode('agentbox-relay-v1');
20
+
21
+ export function b64u(bytes) {
22
+ let s = '';
23
+ for (const b of bytes) s += String.fromCharCode(b);
24
+ return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
25
+ }
26
+
27
+ async function bits(key, info, length = 256) {
28
+ const ikm = await subtle().importKey('raw', enc.encode(String(key)), 'HKDF', false, ['deriveBits']);
29
+ return new Uint8Array(await subtle().deriveBits({ name: 'HKDF', hash: 'SHA-256', salt: SALT, info: enc.encode(info) }, ikm, length));
30
+ }
31
+
32
+ async function roomOf(proof) {
33
+ return b64u(new Uint8Array(await subtle().digest('SHA-256', enc.encode(`agentbox-room:${proof}`))));
34
+ }
35
+
36
+ /** The room the relay knows this pair by, and the Mac's proof that it is the Mac's. */
37
+ export async function relayIds(key) {
38
+ const proof = b64u(await bits(key, 'mac-proof'));
39
+ return { room: await roomOf(proof), proof };
40
+ }
41
+
42
+ /** Is `proof` the proof for `room`? The relay asks this of every Mac. */
43
+ export async function macMayJoin(room, proof) {
44
+ if (typeof room !== 'string' || typeof proof !== 'string' || !proof) return false;
45
+ const want = await roomOf(proof);
46
+ if (want.length !== room.length) return false;
47
+ let diff = 0;
48
+ for (let i = 0; i < want.length; i += 1) diff |= want.charCodeAt(i) ^ room.charCodeAt(i);
49
+ return diff === 0;
50
+ }
51
+
52
+ const aes = async (raw) => subtle().importKey('raw', raw, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']);
53
+
54
+ /** One key for each direction, so nothing sealed one way opens the other. */
55
+ export async function relayKeys(key) {
56
+ return { up: await aes(await bits(key, 'phone-to-mac')), down: await aes(await bits(key, 'mac-to-phone')) };
57
+ }
58
+
59
+ /**
60
+ * Seal `bytes` for the other end. `aad` names what it is (a request, or the
61
+ * n-th frame of the answer to one request), so a frame cannot be passed off
62
+ * as another. Out: 12 bytes of nonce, then the sealed bytes.
63
+ */
64
+ export async function seal(key, bytes, aad) {
65
+ const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
66
+ const ct = new Uint8Array(await subtle().encrypt({ name: 'AES-GCM', iv, additionalData: enc.encode(aad) }, key, bytes));
67
+ const out = new Uint8Array(12 + ct.length);
68
+ out.set(iv, 0);
69
+ out.set(ct, 12);
70
+ return out;
71
+ }
72
+
73
+ /** Open what `seal` made, or throw if a single bit of it was changed. */
74
+ export async function open(key, sealed, aad) {
75
+ if (!(sealed instanceof Uint8Array) || sealed.length < 28) throw new Error('Too short to be sealed.');
76
+ const pt = await subtle().decrypt({ name: 'AES-GCM', iv: sealed.subarray(0, 12), additionalData: enc.encode(aad) }, key, sealed.subarray(12));
77
+ return new Uint8Array(pt);
78
+ }
79
+
80
+ /* ------------------------------------------------------------- the wire */
81
+
82
+ // A request, phone to Mac, before it is sealed: four bytes giving the length of
83
+ // a json head, the head, then the body. The head carries a time and a random
84
+ // nonce, so the Mac can turn away one it has already answered.
85
+ export function packRequest(head, body = new Uint8Array(0)) {
86
+ const h = enc.encode(JSON.stringify(head));
87
+ const out = new Uint8Array(4 + h.length + body.length);
88
+ new DataView(out.buffer).setUint32(0, h.length);
89
+ out.set(h, 4);
90
+ out.set(body, 4 + h.length);
91
+ return out;
92
+ }
93
+
94
+ export function unpackRequest(bytes) {
95
+ const n = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);
96
+ const head = JSON.parse(new TextDecoder().decode(bytes.subarray(4, 4 + n)));
97
+ return { head, body: bytes.subarray(4 + n) };
98
+ }
99
+
100
+ // An answer, Mac to phone, is a run of frames, each sealed on its own: a head
101
+ // (status and headers), the body in pieces, and an end. The end is sealed too,
102
+ // so an answer cut short by anyone in between is seen to be cut short.
103
+ //
104
+ // WAIT comes before the head and says only "still working". A reply to an
105
+ // agent mid-step is answered when the agent takes it, which can be minutes,
106
+ // and the room gives a Mac 30 seconds to start answering. A phone that asks
107
+ // for these (`wait` in its request's head) skips them; one that does not
108
+ // never gets one.
109
+ export const FRAME = Object.freeze({ HEAD: 0, DATA: 1, END: 2, WAIT: 3 });
110
+
111
+ export const frameAad = (nonce, seq) => `res:${nonce}:${seq}`;
112
+
113
+ // ON THE MAC'S SOCKET TO THE RELAY one byte says what a message is, then eight
114
+ // bytes of request number, then whatever it carries:
115
+ // to the Mac 0x01 a request (the sealed bytes) 0x02 the phone gave up
116
+ // 0x03 a piece of a request too big for one message; the
117
+ // 0x01 that follows carries the last piece
118
+ // from the Mac 0x10 one sealed frame of the answer 0x11 done
119
+ // 0x12 refused: it would not open, or it was a replay
120
+ export const TO_MAC = Object.freeze({ REQUEST: 0x01, CANCEL: 0x02, PART: 0x03 });
121
+ /** The most the relay puts in one socket message to the Mac. Cloudflare's limit is 1 MB. */
122
+ export const PART_BYTES = 512 * 1024;
123
+ export const FROM_MAC = Object.freeze({ FRAME: 0x10, DONE: 0x11, REFUSED: 0x12 });
124
+ export const ID_BYTES = 8;
125
+
126
+ export function tag(type, id, rest = new Uint8Array(0)) {
127
+ const out = new Uint8Array(1 + ID_BYTES + rest.length);
128
+ out[0] = type;
129
+ out.set(id, 1);
130
+ out.set(rest, 1 + ID_BYTES);
131
+ return out;
132
+ }
133
+
134
+ // Between the relay and either end each sealed piece goes with its length in
135
+ // front, so one stream of bytes can carry many.
136
+ export function lengthPrefixed(bytes) {
137
+ const out = new Uint8Array(4 + bytes.length);
138
+ new DataView(out.buffer).setUint32(0, bytes.length);
139
+ out.set(bytes, 4);
140
+ return out;
141
+ }
142
+
143
+ /** Split a stream (or a reader of one) of length-prefixed pieces back into the pieces. */
144
+ export async function* pieces(source) {
145
+ const reader = typeof source.getReader === 'function' ? source.getReader() : source;
146
+ let buf = new Uint8Array(0);
147
+ try {
148
+ for (;;) {
149
+ while (buf.length >= 4) {
150
+ const n = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint32(0);
151
+ if (buf.length < 4 + n) break;
152
+ yield buf.slice(4, 4 + n);
153
+ buf = buf.subarray(4 + n);
154
+ }
155
+ const { value, done } = await reader.read();
156
+ if (done) return;
157
+ const next = new Uint8Array(buf.length + value.length);
158
+ next.set(buf, 0);
159
+ next.set(value, buf.length);
160
+ buf = next;
161
+ }
162
+ } finally {
163
+ try { reader.releaseLock(); } catch { /* cancelled under us */ }
164
+ }
165
+ }
@@ -28,6 +28,10 @@ export const REPEAT_LABEL_PREFIX: string;
28
28
  export function repeatLabel(ruleId: string): string;
29
29
  export function ruleIdOf(item: { labels?: string[] } | null | undefined): string | null;
30
30
  export function isCleanRun(item: { labels?: string[] } | null | undefined): boolean;
31
+ export function runToOpen<T extends { id: string; product: string; labels?: string[]; createdAt: number }>(
32
+ rule: { id: string; product: string; lastOccurrence?: string },
33
+ items: T[],
34
+ ): T | null;
31
35
  export const DAY_NAMES: string[];
32
36
  export function isRepeatRule(rule: unknown): boolean;
33
37
  export function ruleLabel(rule: unknown): string;
@@ -43,3 +47,7 @@ export function foldRepeats(
43
47
  lines: Array<{ id: string; ts: number; patch: Record<string, unknown> } | null>,
44
48
  now?: number,
45
49
  ): Map<string, RepeatRule>;
50
+ export const PROTOCOL_OPENS: string;
51
+ export const INSTRUCTION_MARK: string;
52
+ /** A repeating run's body without the agent's rules in front of it; any other body whole. */
53
+ export function instructionOf(body: string | null | undefined): string;
@@ -15,6 +15,16 @@ const KEY_RE = /^\d{4}-\d{2}-\d{2}$/;
15
15
  export const CLEAN_LABEL = 'clean';
16
16
  export const REPEAT_LABEL_PREFIX = 'repeat:';
17
17
 
18
+ // A run's body is the app's rules for the agent (main/repeats.mjs), then the
19
+ // person's instruction after INSTRUCTION_MARK. Screens show the instruction.
20
+ export const PROTOCOL_OPENS = 'THIS IS ONE RUN OF A REPEATING TASK.';
21
+ export const INSTRUCTION_MARK = 'HER INSTRUCTION FOLLOWS.';
22
+ export function instructionOf(body) {
23
+ const text = String(body ?? '');
24
+ const at = text.indexOf(`\n${INSTRUCTION_MARK}\n`);
25
+ return text.startsWith(PROTOCOL_OPENS) && at >= 0 ? text.slice(at + INSTRUCTION_MARK.length + 2).trim() : text;
26
+ }
27
+
18
28
  export function repeatLabel(ruleId) {
19
29
  return `${REPEAT_LABEL_PREFIX}${ruleId}`;
20
30
  }
@@ -24,14 +34,35 @@ export function ruleIdOf(item) {
24
34
  return found ? found.slice(REPEAT_LABEL_PREFIX.length) : null;
25
35
  }
26
36
 
37
+ // THE STANDING INSTRUCTION: the words every run after this one is briefed with.
38
+ // `serve` copies this into each occurrence's body under the protocol, and the
39
+ // store tool an agent changes it through reads and writes the same thing, so
40
+ // what the tool shows a run is what the next run gets.
41
+ export function standingInstruction(rule) {
42
+ return rule?.body ?? rule?.title;
43
+ }
44
+
27
45
  // A clean run is one the worker EXPLICITLY marked clean. Everything else is news
28
46
  // and reaches her, including a run that died without saying anything: this
29
47
  // predicate exists to hide, never to show, so it fails open by construction.
30
48
  export function isCleanRun(item) {
31
- return !!ruleIdOf(item) && (item?.labels ?? []).includes(CLEAN_LABEL);
49
+ const labels = item?.labels ?? [];
50
+ const aRun = !!ruleIdOf(item) || labels.some((l) => typeof l === 'string' && (l.startsWith('teammate:') || l === 'teammate-offer'));
51
+ return aRun && labels.includes(CLEAN_LABEL);
52
+ }
53
+
54
+ // THE THREAD AN OPENED REPEATING TASK LANDS ON: its latest run, an ordinary
55
+ // work item, so the Reply box under it messages that run's agent like any
56
+ // other thread (w-5e3f2e047f). The run it last served first; when that one has
57
+ // left the store's window, the newest of its runs still on file. Null before
58
+ // the first run, and never a run of another rule or another project.
59
+ export function runToOpen(rule, items) {
60
+ const mine = (items ?? []).filter((i) => i.product === rule.product && ruleIdOf(i) === rule.id);
61
+ return mine.find((i) => i.id === rule.lastOccurrence)
62
+ ?? mine.reduce((newest, i) => (!newest || i.createdAt > newest.createdAt ? i : newest), null);
32
63
  }
33
64
 
34
- export const DAY_NAMES = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
65
+ export const DAY_NAMES = ['Sunday','Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
35
66
 
36
67
  // Three shapes, because they are the three a founder actually asks for:
37
68
  // { every: 'day', at } every morning
@@ -0,0 +1,167 @@
1
+ // A ROOM'S WALL AND THE ROW MY MAC PUBLISHES FOR IT (w-3d0fa23ffb).
2
+ //
3
+ // Pure. Rows are as the cloud sends them (cloud/supabase/migrations/
4
+ // 20261021000000_rooms.sql): `room_profiles` say what a person lets a card
5
+ // show, `room_presence` is one row per Mac. docs/team/ROOMS-PLAN.md has the
6
+ // rules; the test is tests/a-room-wall-shows-each-person-as-they-chose.
7
+ import { levelFor, localDate, streakOf } from './agent-hours.mjs';
8
+ import { visibilityOf } from './thread-cards.mjs';
9
+
10
+ export const PRESENCE_STALE_MS = 15 * 60_000;
11
+ export const WORKING_ON_MAX = 64;
12
+ const DAY_MS = 86_400_000;
13
+
14
+ export function displayNameOf(name, { full = false } = {}) {
15
+ let s = String(name ?? '').trim();
16
+ if (s.includes('@')) s = s.split('@')[0];
17
+ const words = s.split(/\s+/).filter(Boolean);
18
+ if (words.length < 2 || full) return words.join(' ');
19
+ return `${words[0]} ${words.at(-1)[0].toUpperCase()}.`;
20
+ }
21
+
22
+ const ms = (v) => (v == null ? NaN : typeof v === 'number' ? v : Date.parse(v));
23
+ // A date the publisher wrote in their own zone, read as local midnight here.
24
+ const dayMs = (d) => (typeof d === 'string' ? new Date(`${d}T00:00:00`).getTime() : NaN);
25
+ const num = (v) => (Number.isFinite(Number(v)) ? Number(v) : 0);
26
+
27
+ function sumPresence(rows, now) {
28
+ let agents = 0, total = 0, week = 0, today = 0;
29
+ const days = new Set();
30
+ const months = new Map();
31
+ let lead = null;
32
+ for (const r of rows) {
33
+ if (r.gone) continue;
34
+ total += num(r.hours_total);
35
+ if (now < dayMs(r.week_start) + 7 * DAY_MS) week += num(r.hours_week);
36
+ if (now < dayMs(r.day) + DAY_MS) today += num(r.hours_today);
37
+ for (const d of r.active_days ?? []) days.add(String(d).slice(0, 10));
38
+ for (const m of Array.isArray(r.hours_by_month) ? r.hours_by_month : []) {
39
+ if (typeof m?.month === 'string') months.set(m.month, (months.get(m.month) ?? 0) + num(m.hours));
40
+ }
41
+ const fresh = now - ms(r.updated_at) < PRESENCE_STALE_MS;
42
+ if (!fresh || !(num(r.agents_running) > 0)) continue;
43
+ agents += num(r.agents_running);
44
+ if (!lead || (ms(r.working_at) || 0) > (ms(lead.working_at) || 0)) lead = r;
45
+ }
46
+ const byMonth = [...months].sort(([a], [b]) => a.localeCompare(b)).map(([month, hours]) => ({ month, hours }));
47
+ return { agents, total, week, today, streak: streakOf([...days], now), byMonth, lead };
48
+ }
49
+
50
+ function workingOf({ profile, lead, teammate, cards }) {
51
+ const none = { workingOn: null, workingPrivate: false };
52
+ if (!lead) return none;
53
+ if (profile.show_working === 'none' || lead.working_private) return { workingOn: null, workingPrivate: true };
54
+ if (profile.show_working === 'everyone' && lead.working_on) return { workingOn: lead.working_on, workingPrivate: false };
55
+ // Running, and no name this viewer may read: it says Private thread.
56
+ const hidden = { workingOn: null, workingPrivate: true };
57
+ if (!teammate) return hidden;
58
+ const card = cards
59
+ .filter((c) => c.state === 'running' && c.visible !== false && c.title)
60
+ .sort((a, b) => num(b.updatedAt) - num(a.updatedAt))[0];
61
+ return card ? { workingOn: String(card.title).slice(0, WORKING_ON_MAX), workingPrivate: false } : hidden;
62
+ }
63
+
64
+ // One card per member with a profile, sorted by name so cards never jump.
65
+ // Invisible people are already hidden from others by the cloud; my own
66
+ // profile still reaches me, so I leave my own wall too. `mine` is what this
67
+ // Mac knows I am working on, which my own card shows whatever others see.
68
+ // `names` is the cloud's room_names for this room, or null on a cloud without
69
+ // it: a member with no card at all is on the wall, named, as not on Rooms yet.
70
+ // In a `listed` room, open to anyone, my own card shows me what strangers see.
71
+ export function wallCards({ members = [], profiles = [], presence = [], teammates = [], teamCards = [], meId = null, now = Date.now(), mine = null, names = null, listed = false }) {
72
+ const inRoom = new Set(members);
73
+ const mates = new Set(teammates);
74
+ const out = [];
75
+ for (const p of profiles) {
76
+ if (!p?.person_id || !inRoom.has(p.person_id)) continue;
77
+ if (now < ms(p.invisible_until)) continue;
78
+ const id = p.person_id;
79
+ const s = sumPresence(presence.filter((r) => r.person_id === id), now);
80
+ const cards = teamCards.filter((c) => c.personId === id);
81
+ out.push({
82
+ id,
83
+ me: id === meId,
84
+ name: p.display_name || '',
85
+ avatarUrl: p.avatar_url ?? null,
86
+ headline: p.headline ?? null,
87
+ bio: p.bio ?? null,
88
+ // My own count is this Mac's, not the cloud's last word on it.
89
+ agents: p.show_agents === false ? null : id === meId && Number.isFinite(mine?.agents) ? mine.agents : s.agents,
90
+ level: p.show_level === false ? null : levelFor(s.total),
91
+ hoursTotal: s.total,
92
+ hoursWeek: s.week,
93
+ hoursToday: s.today,
94
+ streak: s.streak,
95
+ byMonth: s.byMonth,
96
+ teammate: mates.has(id) && id !== meId,
97
+ showActivity: p.show_activity !== false,
98
+ // A list of threads an older Mac still sends is read by nobody.
99
+ activity: null,
100
+ status: p.show_status !== false && p.status_text && !(ms(p.status_until) <= now) ? p.status_text : null,
101
+ ...(id === meId && mine ? ownWorking(mine, listed && p.show_working !== 'everyone')
102
+ : workingOf({ profile: p, lead: s.lead, teammate: mates.has(id) && id !== meId, cards })),
103
+ });
104
+ }
105
+ for (const n of names ?? []) {
106
+ if (n.has_card || !inRoom.has(n.person_id) || n.person_id === meId) continue;
107
+ out.push({
108
+ id: n.person_id, me: false, away: true, name: displayNameOf(n.name), avatarUrl: n.avatar_url ?? null, headline: null, bio: null,
109
+ agents: null, level: null, hoursTotal: 0, hoursWeek: 0, hoursToday: 0, streak: 0, byMonth: [],
110
+ teammate: mates.has(n.person_id), showActivity: false, activity: null, status: null, workingOn: null, workingPrivate: false,
111
+ });
112
+ }
113
+ return out.sort((a, b) => a.name.localeCompare(b.name) || a.id.localeCompare(b.id));
114
+ }
115
+
116
+ const ownWorking = (mine, hidden) => (hidden
117
+ ? { workingOn: null, workingPrivate: !!mine.workingOn }
118
+ : { workingOn: mine.workingOn ?? null, workingPrivate: false });
119
+
120
+ const shortName = (item) => (String(item?.label ?? '').trim() || String(item?.title ?? '')).slice(0, WORKING_ON_MAX);
121
+
122
+ // The presence row this Mac sends. `running` is the supervisor's running
123
+ // sessions ({itemId, product, startedAt}); `summary` is summarizeRuns'.
124
+ // A thread is named only when its project and its own word leave it to the
125
+ // whole team, and only when I let everyone see it: realtime sends whole rows,
126
+ // so a name on the row reaches everyone in my rooms.
127
+ export function myPresence({ running = [], items = [], products = [], summary, now = Date.now(), showWorking = 'everyone' }) {
128
+ const lead = [...running].sort((a, b) => num(b.startedAt) - num(a.startedAt))[0];
129
+ let working_on = null;
130
+ let working_private = false;
131
+ if (lead && showWorking !== 'none') {
132
+ const item = items.find((i) => i.id === lead.itemId);
133
+ const product = products.find((p) => p.slug === (item?.product ?? lead.product));
134
+ const v = item ? visibilityOf(item, product) : 'private';
135
+ if (v !== undefined && v !== 'team') working_private = true;
136
+ else if (showWorking === 'everyone') working_on = shortName(item) || null;
137
+ }
138
+ return {
139
+ agents_running: running.length,
140
+ working_on,
141
+ working_private,
142
+ working_at: lead ? new Date(num(lead.startedAt) || now).toISOString() : null,
143
+ hours_total: round2(summary?.total),
144
+ hours_week: round2(summary?.week),
145
+ week_start: summary?.weekStart ?? localDate(now),
146
+ hours_today: round2(summary?.today),
147
+ day: summary?.day ?? localDate(now),
148
+ streak_days: summary?.streak ?? 0,
149
+ active_days: summary?.activeDays ?? [],
150
+ hours_by_month: summary?.byMonth ?? [],
151
+ // Never a list of threads: it reached everyone in my rooms.
152
+ activity: [],
153
+ };
154
+ }
155
+
156
+ const round2 = (v) => Math.round(num(v) * 100) / 100;
157
+
158
+ // A switch turned off keeps that value off the cloud, so nobody can read it
159
+ // from the row, a realtime event or an invite preview.
160
+ export function withoutHidden(row, profile) {
161
+ const out = { ...row };
162
+ if (profile?.show_agents === false) out.agents_running = 0;
163
+ if (profile?.show_level === false) {
164
+ Object.assign(out, { hours_total: 0, hours_week: 0, hours_today: 0, streak_days: 0, active_days: [], hours_by_month: [] });
165
+ }
166
+ return out;
167
+ }
@@ -0,0 +1,4 @@
1
+ export const JOIN_SCHEME: string;
2
+ export const JOIN_SITE: string;
3
+ export function joinLink(token: string): string;
4
+ export function tokenFromLink(text: unknown): string | null;
@@ -0,0 +1,21 @@
1
+ // A room's invite link (docs/team/ROOMS-PLAN.md, Joining): the web address
2
+ // people share, and the app's own scheme that page opens.
3
+ import { nameSlug } from './product-name.mjs';
4
+
5
+ export const JOIN_SCHEME = nameSlug;
6
+ export const JOIN_SITE = `https://${nameSlug}.ac/join/`;
7
+
8
+ export const joinLink = (token) => `${JOIN_SITE}${token}`;
9
+
10
+ /** Any link on the app's own scheme: a room invite, or Stripe's way back. */
11
+ export const ourLink = (url) => typeof url === 'string' && url.startsWith(`${JOIN_SCHEME}://`);
12
+
13
+ const TOKEN = /^[A-Za-z0-9_-]{16,64}$/;
14
+
15
+ /** The token in a pasted link or a deep link, or the token itself; null for anything else. */
16
+ export function tokenFromLink(text) {
17
+ const s = String(text ?? '').trim();
18
+ if (TOKEN.test(s)) return s;
19
+ const m = /^(?:https?:\/\/[^/\s]+\/join\/|[a-z][a-z0-9+.-]*:\/\/join\/)([A-Za-z0-9_-]+)\/?(?:[?#].*)?$/i.exec(s);
20
+ return m && TOKEN.test(m[1]) ? m[1] : null;
21
+ }
@@ -1,20 +1,10 @@
1
1
  export interface RunsOn {
2
- /** 'claude' or 'codex', or null on a Mac where nothing is signed in. */
2
+ /** 'claude' or 'codex', or null on a computer where nothing is signed in. */
3
3
  engine?: string | null;
4
- /**
5
- * The plan in words, as the tool itself reports it: 'Max 20x', 'Pro'. Null
6
- * where the file could not be read, and then only the name is drawn. */
7
- plan?: string | null;
8
4
  }
9
5
 
10
6
  /** The word for the plan behind an engine, or null for one we cannot name. */
11
7
  export declare function runsOnName(engine?: string | null): string | null;
12
8
 
13
- /** The glance line: 'Claude · Max 20x', or 'Claude' with no plan. Null for null. */
14
- export declare function runsOnLine(facts?: RunsOn): string | null;
15
-
16
- /** The whole sentence, for a hover and for a screen reader. Null for null. */
17
- export declare function runsOnTitle(facts?: RunsOn): string | null;
18
-
19
- /** The one line the walk says on a Mac that was already set up. Null for null. */
20
- export declare function foundOnThisMac(facts?: RunsOn): string | null;
9
+ /** The one line the walk says where it was already set up. Null for null. */
10
+ export declare function foundOnThisComputer(facts?: RunsOn): string | null;
@@ -1,30 +1,39 @@
1
- // WHAT THE AGENTS ARE RUNNING ON, IN WORDS.
1
+ // THE ONE LINE SAYING IT FOUND AN ACCOUNT ALREADY ON THIS COMPUTER.
2
2
  //
3
- // From a session with a new user, 2026-10-07: "He didn't realize it
4
- // auto-connected to Claude/Codex; he wasn't sure how it was even running. Would
5
- // be nice to have some small indication."
3
+ // From a session with a new user, 2026-10-07, in her words: a new user came in,
4
+ // started running an agent, and was "like, 'Oh my God, where are the tokens
5
+ // coming from? I didn't even connect my account.'"
6
6
  //
7
7
  // He was right that nothing said. The app finds whichever coding agent is
8
- // already signed in on the Mac and runs every worker on that subscription
8
+ // already signed in on the computer and runs every worker on that subscription
9
9
  // (main/claude-plan.mjs, main/codex-account.mjs), and the walk is SILENT about
10
10
  // it by design: `needsPlan` in renderer/src/plan-setup.ts skips the plan
11
11
  // question entirely when one tool is found and signed in, which is the right
12
- // call and leaves nobody told. The only screen that has ever named the account
13
- // is Settings, two clicks in, and you have to suspect there is something to
14
- // look for before you go.
12
+ // call and leaves nobody told.
13
+ //
14
+ // IT IS A ONE-TIME SURPRISE, SO IT GETS A ONE-TIME ANSWER. A row saying this
15
+ // permanently, in the sidebar's foot, is what this file held first; it shipped
16
+ // on 2026-10-07 and came out the same day. Her words: "we have to get rid of
17
+ // this. It should never say that on the sidebar." Somebody asks where the
18
+ // tokens are coming from ONCE, on their first day, so the place for the answer
19
+ // is the walk they are standing in.
20
+ //
21
+ // AND IT NAMES NO PLAN. The row said "Claude · Max 20x" and that was the half
22
+ // she wanted gone most: "very critically, I want to get rid of that Claude Max
23
+ // 20x plan." The question being answered is "did it connect to something of
24
+ // mine", not "what am I paying for", and the second one is already answered
25
+ // properly on each agent's own page in Settings, where somebody goes when they
26
+ // want it.
15
27
  //
16
28
  // THE PLAN'S VOCABULARY, NOT THE TOOL'S. "Claude" and "ChatGPT" here, never
17
29
  // "Claude Code" and "Codex", which is the rule the walk already follows
18
30
  // (renderer/src/plan-setup.ts: words people know, "never the names of the tools
19
- // underneath"). The fact being shown is WHOSE SUBSCRIPTION IS BEING SPENT, and
20
- // nobody pays for a CLI. The tool names stay where they belong: on a byline
21
- // about which agent is working a row (`engineWordFor`, renderer/src/byline.ts),
22
- // which is a different question with a different answer.
31
+ // underneath"). The tool names stay where they belong: on a byline about which
32
+ // agent is working a row (`engineWordFor`, renderer/src/byline.ts).
23
33
  //
24
- // NOTHING IS SAID UNTIL SOMETHING IS KNOWN. Every function here answers null on
25
- // an engine it cannot name, and the screens draw nothing at all for null rather
26
- // than "checking…" or a dash. A corner that says it does not know is worse than
27
- // a corner that says nothing: it spends the glance and answers no question.
34
+ // NOTHING IS SAID UNTIL SOMETHING IS KNOWN. Both functions answer null on an
35
+ // engine they cannot name, and the walk draws nothing at all for null rather
36
+ // than "checking…" or a dash.
28
37
 
29
38
  /** The word for the plan behind an engine. Null for an engine we cannot name. */
30
39
  export function runsOnName(engine) {
@@ -34,46 +43,16 @@ export function runsOnName(engine) {
34
43
  }
35
44
 
36
45
  /**
37
- * THE LINE ON A GLANCE SURFACE. "Claude · Max 20x", or just "Claude" where the
38
- * plan cannot be read.
39
- *
40
- * The plan is the raw label the tool itself reports, already turned into words
41
- * by whoever read it (`planLabel` in main/claude-plan.mjs for Claude, the
42
- * named-plan table in main/codex-account.mjs for ChatGPT). It is never
43
- * invented here: a plan nobody measured, printed confidently, is the app
44
- * lying about what somebody is paying for.
45
- */
46
- export function runsOnLine({ engine = null, plan = null } = {}) {
47
- const name = runsOnName(engine);
48
- if (!name) return null;
49
- const words = String(plan ?? '').trim();
50
- return words ? `${name} · ${words}` : name;
51
- }
52
-
53
- /**
54
- * THE WHOLE SENTENCE, for a hover and for whatever reads the screen aloud. The
55
- * glance surface carries two words and a plan; this is where the rest of the
56
- * answer lives, and it is the half that tells a new person what they are
57
- * looking at.
58
- */
59
- export function runsOnTitle({ engine = null, plan = null } = {}) {
60
- const name = runsOnName(engine);
61
- if (!name) return null;
62
- const words = String(plan ?? '').trim();
63
- const plansentence = words ? ` Your ${words} plan.` : '';
64
- return `Your agents run on the ${name} account already signed in on this Mac.${plansentence}`;
65
- }
66
-
67
- /**
68
- * THE ONE LINE DURING THE WALK, on the Mac where there was nothing to ask.
46
+ * THE ONE LINE DURING THE WALK, on the computer where there was nothing to ask.
69
47
  *
70
48
  * It replaces silence, not a question: somebody with no coding agent set up
71
49
  * still gets the plan question and the setup card, and never sees this.
50
+ *
51
+ * It says computer rather than Mac: Agentbox runs on Linux too, and one
52
+ * sentence beats a branch, since "this computer" is true on a Mac as well.
72
53
  */
73
- export function foundOnThisMac({ engine = null, plan = null } = {}) {
54
+ export function foundOnThisComputer({ engine = null } = {}) {
74
55
  const name = runsOnName(engine);
75
56
  if (!name) return null;
76
- const words = String(plan ?? '').trim();
77
- if (words) return `Your agents will run on the ${name} ${words} plan already signed in on this Mac.`;
78
- return `Your agents will run on the ${name} account already signed in on this Mac.`;
57
+ return `Your agents will run on the ${name} account already signed in on this computer.`;
79
58
  }
@@ -0,0 +1,4 @@
1
+ // Types for shared/shipped-note.mjs.
2
+
3
+ export function shippedNote(sha?: string | null): string;
4
+ export function isShippedNote(text: unknown): boolean;
@@ -0,0 +1,6 @@
1
+ // The note the app writes on a row once its ship lands (main/store.mjs), and
2
+ // the one reader that has to know it is the app's and not an agent's
3
+ // (renderer/src/format.ts, offerIsLive).
4
+ export const shippedNote = (sha) => (sha ? `Shipped to main as ${sha}.` : 'Shipped to main.');
5
+
6
+ export const isShippedNote = (text) => /^Shipped to main(?: as [0-9a-f]{7,40})?\.$/.test(String(text ?? '').trim());