agentbox-app 0.1.13 → 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 (278) 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/agent-sessions.mjs +282 -268
  9. package/main/agents.mjs +179 -466
  10. package/main/analytics.mjs +12 -2
  11. package/main/answer-modes.mjs +103 -37
  12. package/main/approval-prompt-core.mjs +197 -0
  13. package/main/approval-prompt-server.mjs +4 -138
  14. package/main/approvals.mjs +101 -297
  15. package/main/artifact-path.mjs +47 -18
  16. package/main/claude-input.mjs +48 -4
  17. package/main/claude-plan.mjs +48 -0
  18. package/main/claude-session-open.mjs +28 -0
  19. package/main/claude-usage-process.mjs +40 -0
  20. package/main/claude-usage.mjs +18 -40
  21. package/main/cloud/account.mjs +179 -0
  22. package/main/cloud/aws.mjs +127 -0
  23. package/main/cloud/browser-return.mjs +58 -0
  24. package/main/cloud/clients.mjs +15 -0
  25. package/main/cloud/codex-cloud.mjs +53 -0
  26. package/main/cloud/config.mjs +107 -0
  27. package/main/cloud/docs.mjs +219 -0
  28. package/main/cloud/exec.mjs +27 -0
  29. package/main/cloud/host.mjs +165 -0
  30. package/main/cloud/kit.mjs +80 -0
  31. package/main/cloud/logins.mjs +543 -0
  32. package/main/cloud/managed.mjs +115 -0
  33. package/main/cloud/marker.mjs +104 -0
  34. package/main/cloud/org.mjs +53 -0
  35. package/main/cloud/reuse.mjs +208 -0
  36. package/main/cloud/run-cli.mjs +177 -0
  37. package/main/cloud/run.mjs +1934 -0
  38. package/main/cloud/usage.mjs +76 -0
  39. package/main/cloud-permission.mjs +51 -0
  40. package/main/codex-account.mjs +78 -0
  41. package/main/codex-app-server.mjs +276 -409
  42. package/main/codex-approvals.mjs +148 -433
  43. package/main/codex-models.mjs +20 -2
  44. package/main/codex-session.mjs +212 -507
  45. package/main/codex-threads-worker.mjs +12 -0
  46. package/main/codex-threads.mjs +71 -3
  47. package/main/codex-usage-file.mjs +5 -3
  48. package/main/codex-usage.mjs +56 -7
  49. package/main/codex-watch.mjs +17 -3
  50. package/main/codex.mjs +95 -363
  51. package/main/config.mjs +11 -1
  52. package/main/corner-tag.mjs +340 -19
  53. package/main/dev-window.mjs +1 -1
  54. package/main/engine-bin.mjs +232 -282
  55. package/main/engine-setup.mjs +21 -9
  56. package/main/feedback.mjs +3 -25
  57. package/main/folder-pictures.mjs +2 -2
  58. package/main/freeze-recorder-thread.mjs +86 -0
  59. package/main/freeze-recorder.mjs +146 -0
  60. package/main/fresh-user.mjs +20 -9
  61. package/main/git-change-offthread.mjs +17 -3
  62. package/main/git-change-thread.mjs +5 -2
  63. package/main/harnesses/claude.mjs +32 -11
  64. package/main/harnesses/codex.mjs +9 -2
  65. package/main/importable-threads.mjs +64 -6
  66. package/main/ipc.mjs +362 -111
  67. package/main/leftovers.mjs +117 -25
  68. package/main/liquid-glass.mjs +26 -0
  69. package/main/live-replies.mjs +10 -4
  70. package/main/main-went-red.mjs +330 -0
  71. package/main/main.mjs +199 -84
  72. package/main/memory-gate-history.mjs +36 -10
  73. package/main/memory-gate-server.mjs +62 -0
  74. package/main/memory-gate.mjs +14 -1
  75. package/main/menu.mjs +4 -2
  76. package/main/message-priority.mjs +2 -2
  77. package/main/mic.mjs +38 -0
  78. package/main/native/liquid-glass/build/liquid-glass.node +0 -0
  79. package/main/new-chats-watch.mjs +110 -0
  80. package/main/new-user-window.mjs +25 -0
  81. package/main/notify.mjs +24 -8
  82. package/main/phone-alerts.mjs +117 -0
  83. package/main/phone-inbox.mjs +625 -0
  84. package/main/phone-link.mjs +29 -9
  85. package/main/phone-uploads.mjs +476 -0
  86. package/main/picture-shape.mjs +114 -0
  87. package/main/quit-sequence.mjs +143 -0
  88. package/main/refresh-agent-models.mjs +15 -2
  89. package/main/relay-client.mjs +69 -1
  90. package/main/repeats.mjs +10 -4
  91. package/main/row-label.mjs +12 -41
  92. package/main/run-change.mjs +13 -0
  93. package/main/run-ledger.mjs +135 -0
  94. package/main/save-attachments.mjs +41 -0
  95. package/main/serve.mjs +45 -3
  96. package/main/settings.mjs +220 -44
  97. package/main/shell-path.mjs +185 -37
  98. package/main/ship-conflict-brief.mjs +119 -0
  99. package/main/ship-queue.mjs +115 -19
  100. package/main/source-updater.mjs +18 -10
  101. package/main/stage-attachment.mjs +68 -0
  102. package/main/stall-watch.mjs +50 -0
  103. package/main/store/home.mjs +87 -232
  104. package/main/store/paths.mjs +11 -0
  105. package/main/store/teammates-files.mjs +99 -0
  106. package/main/store/work-items.mjs +231 -13
  107. package/main/store.mjs +226 -52
  108. package/main/supervisor.mjs +3103 -341
  109. package/main/task-folders.mjs +430 -30
  110. package/main/task-terminals.mjs +110 -34
  111. package/main/team/index.mjs +114 -10
  112. package/main/team/memory-cloud.mjs +183 -1
  113. package/main/team/room-hours.mjs +48 -0
  114. package/main/team/room-setup.mjs +31 -0
  115. package/main/team/rooms-memory.mjs +454 -0
  116. package/main/team/rooms-shape.mjs +42 -0
  117. package/main/team/rooms-supabase.mjs +324 -0
  118. package/main/team/rooms.mjs +575 -0
  119. package/main/team/session.mjs +96 -26
  120. package/main/team/supabase-backend.mjs +180 -0
  121. package/main/team/sync.mjs +109 -1
  122. package/main/teammates.mjs +580 -0
  123. package/main/terminal-host.mjs +143 -0
  124. package/main/terminal-hosts.mjs +261 -0
  125. package/main/transcript-search.mjs +80 -0
  126. package/main/webview-guard.mjs +45 -0
  127. package/main/worktree-include.mjs +90 -219
  128. package/mcp/core/documents.mjs +3 -1
  129. package/mcp/core/repeats.mjs +88 -0
  130. package/mcp/core/teammates.mjs +222 -0
  131. package/mcp/core/work.mjs +76 -4
  132. package/mcp/stdio.mjs +24 -11
  133. package/mcp/tools.mjs +116 -8
  134. package/package.json +21 -5
  135. package/renderer/dist/assets/{abnfDiagram-YUKMZFIV-DPPuMWt-.js → abnfDiagram-YUKMZFIV-CoWB6YUK.js} +1 -1
  136. package/renderer/dist/assets/{arc-HmFfdseH.js → arc-Ch0NrCul.js} +1 -1
  137. package/renderer/dist/assets/{architectureDiagram-47P4ROYG-DxJ3zHrH.js → architectureDiagram-47P4ROYG-CXz9M5tQ.js} +1 -1
  138. package/renderer/dist/assets/{blockDiagram-E7TT5TSB-CbTXzOqQ.js → blockDiagram-E7TT5TSB-DchGUuHH.js} +1 -1
  139. package/renderer/dist/assets/{c4Diagram-CRA5TL53-X5ItFyLn.js → c4Diagram-CRA5TL53-Cip-zNyr.js} +1 -1
  140. package/renderer/dist/assets/channel-BQuQSL--.js +1 -0
  141. package/renderer/dist/assets/{chunk-2Q5K7J3B-DC12Ho_L.js → chunk-2Q5K7J3B-BoXid_2r.js} +1 -1
  142. package/renderer/dist/assets/{chunk-5VM5RSS4-ClVtfPd0.js → chunk-5VM5RSS4-Cpez1rRz.js} +1 -1
  143. package/renderer/dist/assets/{chunk-7TKQ45FW-DgcOm0HT.js → chunk-7TKQ45FW-DKA84YkE.js} +1 -1
  144. package/renderer/dist/assets/{chunk-AW2ZBBNX-C01s4yoF.js → chunk-AW2ZBBNX-Dg-SumPG.js} +1 -1
  145. package/renderer/dist/assets/{chunk-FVRAUYC3-Bj0zxhR4.js → chunk-FVRAUYC3-06TCvPm1.js} +2 -2
  146. package/renderer/dist/assets/{chunk-HTAEGDNF-DWDeEhLU.js → chunk-HTAEGDNF-CMEBy1Td.js} +1 -1
  147. package/renderer/dist/assets/{chunk-JWPE2WC7-Dsqwv3eW.js → chunk-JWPE2WC7-BJ2ObmV-.js} +1 -1
  148. package/renderer/dist/assets/{chunk-KQW6MTUR-CE86ANL2.js → chunk-KQW6MTUR-CGWOYXUE.js} +1 -1
  149. package/renderer/dist/assets/{chunk-XXDRQBXY-CoNTASfs.js → chunk-XXDRQBXY-CiZp07XC.js} +1 -1
  150. package/renderer/dist/assets/{classDiagram-v2-K4WV4PDN-2wprLqtw.js → classDiagram-v2-K4WV4PDN-DJNIX719.js} +1 -1
  151. package/renderer/dist/assets/cornerTag-CPgvu-q3.js +1 -0
  152. package/renderer/dist/assets/cornerTag-oMfEjzCO.css +1 -0
  153. package/renderer/dist/assets/{cose-bilkent-JH36ORCC-enqMiYbn.js → cose-bilkent-JH36ORCC-CbkyZOUH.js} +1 -1
  154. package/renderer/dist/assets/{cynefin-EF2NZ3EQ-DHngLxvQ.js → cynefin-EF2NZ3EQ-78iOroZO.js} +1 -1
  155. package/renderer/dist/assets/{cynefinDiagram-3GCD6N5R-C0rZZIl3.js → cynefinDiagram-3GCD6N5R-GcCopMs7.js} +1 -1
  156. package/renderer/dist/assets/{dagre-W4DXFKR2-DQnaEWO7.js → dagre-W4DXFKR2-DHLlXLkz.js} +1 -1
  157. package/renderer/dist/assets/{diagram-2UJZ2QOL-CS8AlGPx.js → diagram-2UJZ2QOL-DuZSUHcH.js} +1 -1
  158. package/renderer/dist/assets/{diagram-OVF4WLC6-yiDMHIkY.js → diagram-OVF4WLC6-Ckxu3cqH.js} +1 -1
  159. package/renderer/dist/assets/{diagram-PFPMY2P6-XbQKzbBA.js → diagram-PFPMY2P6-CsTw9Ycn.js} +1 -1
  160. package/renderer/dist/assets/{diagram-UMYRVEAY-dhVvlfl_.js → diagram-UMYRVEAY-CTXd8qVg.js} +1 -1
  161. package/renderer/dist/assets/{diagram-YEKJPTXX-DZHe8SCf.js → diagram-YEKJPTXX-CJs1T_9m.js} +1 -1
  162. package/renderer/dist/assets/{diagram-ZIFT7M5P-B5h6aXVR.js → diagram-ZIFT7M5P-CvjI23mf.js} +1 -1
  163. package/renderer/dist/assets/{ebnfDiagram-VR2GEFS7-PR9ScDco.js → ebnfDiagram-VR2GEFS7-d_IeAzOJ.js} +1 -1
  164. package/renderer/dist/assets/{elk-IJKZMXRS-Cg-yQXPd.js → elk-IJKZMXRS-zo9KFP3N.js} +1 -1
  165. package/renderer/dist/assets/{erDiagram-O2IAPWRE-omOrJMxu.js → erDiagram-O2IAPWRE-DqfYf5Dt.js} +1 -1
  166. package/renderer/dist/assets/{flowDiagram-OXPTDLAJ-aOX2hzvT.js → flowDiagram-OXPTDLAJ-DAWsMLmo.js} +1 -1
  167. package/renderer/dist/assets/{ganttDiagram-R7TSEDQI-B1H3pWyI.js → ganttDiagram-R7TSEDQI-4ml_bhwR.js} +1 -1
  168. package/renderer/dist/assets/{gitGraphDiagram-XJZIOB7I-Cvw304HC.js → gitGraphDiagram-XJZIOB7I-B_WcqSId.js} +1 -1
  169. package/renderer/dist/assets/{infoDiagram-5W2HQ5XZ-_ZXcNViH.js → infoDiagram-5W2HQ5XZ-CI-jYqzc.js} +1 -1
  170. package/renderer/dist/assets/{ishikawaDiagram-K3B6WC7H-C7Mx_qQk.js → ishikawaDiagram-K3B6WC7H-B56VWRlV.js} +1 -1
  171. package/renderer/dist/assets/{journeyDiagram-COXZFDF6-fr62QoAt.js → journeyDiagram-COXZFDF6-D1-p09Lr.js} +1 -1
  172. package/renderer/dist/assets/{kanban-definition-P3RFRI5V-J8zsabCe.js → kanban-definition-P3RFRI5V-Cv3w7JoN.js} +1 -1
  173. package/renderer/dist/assets/{linear-BmNdEtIi.js → linear-C35V2Spi.js} +1 -1
  174. package/renderer/dist/assets/main-BcoBiGwv.css +32 -0
  175. package/renderer/dist/assets/main-CLnIOulD.js +1191 -0
  176. package/renderer/dist/assets/{mermaid.core-CnYzvFkz.js → mermaid.core-BzGT5Vo9.js} +4 -4
  177. package/renderer/dist/assets/{mindmap-definition-LZFPQGKD-Bfcxc7PM.js → mindmap-definition-LZFPQGKD-BZUfuymC.js} +1 -1
  178. package/renderer/dist/assets/{pegDiagram-BGZESJAR-CwUJOq6X.js → pegDiagram-BGZESJAR-DFh9XNGe.js} +1 -1
  179. package/renderer/dist/assets/{pieDiagram-CAPJLFHJ-BrwMmqfC.js → pieDiagram-CAPJLFHJ-C65tPVsy.js} +1 -1
  180. package/renderer/dist/assets/{product-name-CTKLYNWY.js → product-name-CSNW6d7s.js} +1 -1
  181. package/renderer/dist/assets/{quadrantDiagram-GDMTTRAM-BN4Stoj-.js → quadrantDiagram-GDMTTRAM-Cushj_nk.js} +1 -1
  182. package/renderer/dist/assets/{railroadDiagram-SM67HX2B-CPZ5bbeR.js → railroadDiagram-SM67HX2B-DZGebGx5.js} +1 -1
  183. package/renderer/dist/assets/{requirementDiagram-X7JNWC4A-CgRnimVe.js → requirementDiagram-X7JNWC4A-DpAB0MNo.js} +1 -1
  184. package/renderer/dist/assets/{sankeyDiagram-UM26HJRW-Bt2rej__.js → sankeyDiagram-UM26HJRW-D-29rHra.js} +1 -1
  185. package/renderer/dist/assets/{sequenceDiagram-ZO4K6R2Y-Dd5nKe9G.js → sequenceDiagram-ZO4K6R2Y-FfOQAZol.js} +1 -1
  186. package/renderer/dist/assets/{sizeCapture-INFHLROL-usyNyXyh.js → sizeCapture-INFHLROL-C_n6fDBY.js} +1 -1
  187. package/renderer/dist/assets/{stateDiagram-v2-TBUQTH76-CRzUBb0u.js → stateDiagram-v2-TBUQTH76-BZjenzJG.js} +1 -1
  188. package/renderer/dist/assets/{swimlanes-N4OXWK64-C1GJvMS0.js → swimlanes-N4OXWK64-DxaMU9vX.js} +1 -1
  189. package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cb2kM6rr.js +8 -0
  190. package/renderer/dist/assets/{timeline-definition-YOQAKGHF-caeyPlcc.js → timeline-definition-YOQAKGHF-DTg5HJKz.js} +1 -1
  191. package/renderer/dist/assets/{usecaseDiagram-VIAY4XPW-BN_SV6PC.js → usecaseDiagram-VIAY4XPW-Bc3MZDsl.js} +1 -1
  192. package/renderer/dist/assets/{vennDiagram-BLWOH2XV-BLGuaUX-.js → vennDiagram-BLWOH2XV-GIVGk9Id.js} +1 -1
  193. package/renderer/dist/assets/{wardleyDiagram-YMQ3BMBF-Da0cJeNc.js → wardleyDiagram-YMQ3BMBF-NmJnbQOg.js} +1 -1
  194. package/renderer/dist/assets/{xychartDiagram-TAQBALBS-cGfUffMk.js → xychartDiagram-TAQBALBS-p-kfGfnW.js} +1 -1
  195. package/renderer/dist/corner-tag.html +3 -3
  196. package/renderer/dist/index.html +3 -3
  197. package/scripts/after-install.mjs +30 -8
  198. package/scripts/cloud-shim.pl +57 -0
  199. package/shared/account-names.d.mts +6 -0
  200. package/shared/account-names.mjs +15 -0
  201. package/shared/agent-hours.mjs +136 -0
  202. package/shared/agent-import.mjs +60 -34
  203. package/shared/agents.mjs +126 -522
  204. package/shared/analytics-events.mjs +12 -0
  205. package/shared/approval-card.mjs +36 -0
  206. package/shared/bridge-map.mjs +27 -1
  207. package/shared/claude-code-shape.mjs +6 -0
  208. package/shared/claude-usage.d.mts +1 -0
  209. package/shared/claude-usage.mjs +42 -1
  210. package/shared/cloud-hand-back.d.mts +7 -0
  211. package/shared/cloud-hand-back.mjs +20 -0
  212. package/shared/cloud-kit.d.mts +219 -0
  213. package/shared/cloud-kit.mjs +341 -0
  214. package/shared/cloud-login-name.mjs +4 -0
  215. package/shared/cloud-start.mjs +17 -0
  216. package/shared/cloud-versions.mjs +23 -0
  217. package/shared/codex-import.mjs +2 -2
  218. package/shared/codex-modes.d.mts +22 -9
  219. package/shared/codex-modes.mjs +67 -11
  220. package/shared/codex-usage.mjs +108 -2
  221. package/shared/corner-tag.d.mts +23 -1
  222. package/shared/corner-tag.mjs +284 -29
  223. package/shared/dead-run-trace.mjs +19 -2
  224. package/shared/engines.mjs +70 -309
  225. package/shared/feedback.d.mts +7 -0
  226. package/shared/feedback.mjs +25 -0
  227. package/shared/first-run-practice.mjs +17 -24
  228. package/shared/harness-definitions.mjs +8 -0
  229. package/shared/instruction-defaults.json +2 -2
  230. package/shared/key-caps.d.mts +14 -0
  231. package/shared/key-caps.mjs +68 -0
  232. package/shared/message-edits.d.mts +7 -0
  233. package/shared/message-edits.mjs +35 -0
  234. package/shared/move-thread.d.mts +24 -0
  235. package/shared/move-thread.mjs +112 -0
  236. package/shared/named-threads.mjs +72 -0
  237. package/shared/not-shipped.d.mts +22 -0
  238. package/shared/not-shipped.mjs +89 -0
  239. package/shared/notify-rules.d.mts +6 -0
  240. package/shared/notify-rules.mjs +36 -8
  241. package/shared/phone-files.mjs +28 -0
  242. package/shared/phone-settings.mjs +118 -0
  243. package/shared/phone-snapshot.d.mts +16 -0
  244. package/shared/phone-snapshot.mjs +348 -0
  245. package/shared/picture-formats.d.mts +1 -0
  246. package/shared/picture-formats.mjs +13 -0
  247. package/shared/rank.d.mts +2 -1
  248. package/shared/rank.mjs +32 -19
  249. package/shared/repeats.d.mts +8 -0
  250. package/shared/repeats.mjs +33 -2
  251. package/shared/room-cards.mjs +167 -0
  252. package/shared/room-links.d.mts +4 -0
  253. package/shared/room-links.mjs +21 -0
  254. package/shared/runs-on.d.mts +3 -3
  255. package/shared/runs-on.mjs +8 -5
  256. package/shared/shipped-note.d.mts +4 -0
  257. package/shared/shipped-note.mjs +6 -0
  258. package/shared/shortcuts.mjs +14 -6
  259. package/shared/spawn-trouble.d.mts +4 -0
  260. package/shared/spawn-trouble.mjs +443 -40
  261. package/shared/team-rules.mjs +9 -2
  262. package/shared/teammates.d.mts +19 -0
  263. package/shared/teammates.mjs +756 -0
  264. package/shared/terminal-focus.d.mts +1 -0
  265. package/shared/terminal-focus.mjs +13 -0
  266. package/shared/thread-cards.mjs +4 -1
  267. package/shared/usage.d.mts +9 -0
  268. package/shared/usage.mjs +49 -3
  269. package/shared/websites.d.mts +15 -0
  270. package/shared/websites.mjs +61 -0
  271. package/shared/work-items.mjs +127 -30
  272. package/shared/work-lines.mjs +92 -244
  273. package/renderer/dist/assets/channel-BlT2g9Sz.js +0 -1
  274. package/renderer/dist/assets/cornerTag-BLrxiDE5.css +0 -1
  275. package/renderer/dist/assets/cornerTag-BOT6u3Tn.js +0 -1
  276. package/renderer/dist/assets/main-ChNdnUvt.js +0 -1132
  277. package/renderer/dist/assets/main-nj0QR5kg.css +0 -32
  278. package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cy4Av2_p.js +0 -8
@@ -0,0 +1,76 @@
1
+ // WHAT THE CLOUD RUNS USED, KEPT ON THE MAC (tests/a-cloud-run-records-what-it-really-used).
2
+ //
3
+ // One line per run in ~/.agentbox/cloud-usage.jsonl, beside cloud.json: when,
4
+ // which row, which engine, and what the sandbox measured (cloud/sandbox/usage.mjs).
5
+ // The plan limits are set from these lines, so nothing here is estimated
6
+ // except the cost, and that says so in its name.
7
+ import fs from 'node:fs';
8
+ import os from 'node:os';
9
+ import path from 'node:path';
10
+
11
+ // AgentCore Runtime's published rates (us-east-1, 2026-10), per vCPU-hour of
12
+ // CPU actually used and per GB-hour of memory. Runtime v1, which this account
13
+ // runs; v2 is $0.1276 and $0.0169.
14
+ export const AGENTCORE_RATES = { vcpuHour: 0.0895, gbHour: 0.00945 };
15
+
16
+ export function cloudUsagePath({ home = os.homedir() } = {}) {
17
+ return path.join(home || os.homedir(), '.agentbox', 'cloud-usage.jsonl');
18
+ }
19
+
20
+ export function appendCloudUsage({ runId, item, engine, exitCode, usage }, { home, now = Date.now } = {}) {
21
+ const file = cloudUsagePath({ home });
22
+ const line = {
23
+ at: new Date(now()).toISOString(),
24
+ runId: runId ?? null,
25
+ item: item ?? null,
26
+ engine: engine ?? null,
27
+ exitCode: Number.isInteger(exitCode) ? exitCode : null,
28
+ wallSeconds: usage?.wallSeconds ?? null,
29
+ cpuSeconds: usage?.cpuSeconds ?? null,
30
+ peakMemoryBytes: usage?.peakMemoryBytes ?? null,
31
+ source: usage?.source ?? null,
32
+ };
33
+ // Once per run: a homecoming retried after a failed import reads the same
34
+ // done.json again.
35
+ if (line.runId && readCloudUsage({ home }).some((l) => l.runId === line.runId)) return null;
36
+ try {
37
+ fs.mkdirSync(path.dirname(file), { recursive: true });
38
+ fs.appendFileSync(file, JSON.stringify(line) + '\n');
39
+ } catch { /* a record that cannot be written never fails the run */ }
40
+ return line;
41
+ }
42
+
43
+ export function readCloudUsage({ home, file = cloudUsagePath({ home }) } = {}) {
44
+ let text;
45
+ try { text = fs.readFileSync(file, 'utf8'); } catch { return []; }
46
+ const out = [];
47
+ for (const l of text.split('\n')) {
48
+ if (!l.trim()) continue;
49
+ try { out.push(JSON.parse(l)); } catch { /* one broken line loses only itself */ }
50
+ }
51
+ return out;
52
+ }
53
+
54
+ // Agent-hours since `since`, and the cost of the runs that measured
55
+ // themselves. Memory is billed on its high-water mark for the whole run, the
56
+ // way Runtime v1 bills it.
57
+ export function summarizeCloudUsage(lines, { since = 0 } = {}) {
58
+ let runs = 0; let seconds = 0; let measured = 0; let cost = 0;
59
+ for (const l of lines) {
60
+ if (Date.parse(l.at) < since) continue;
61
+ runs += 1;
62
+ seconds += l.wallSeconds || 0;
63
+ if (l.wallSeconds && l.cpuSeconds != null && l.peakMemoryBytes != null) {
64
+ measured += l.wallSeconds;
65
+ cost += (l.cpuSeconds / 3600) * AGENTCORE_RATES.vcpuHour
66
+ + (l.peakMemoryBytes / 1024 ** 3) * (l.wallSeconds / 3600) * AGENTCORE_RATES.gbHour;
67
+ }
68
+ }
69
+ return {
70
+ runs,
71
+ agentHours: Math.round((seconds / 3600) * 100) / 100,
72
+ measuredHours: Math.round((measured / 3600) * 100) / 100,
73
+ estimatedCost: cost,
74
+ costPerAgentHour: measured ? cost / (measured / 3600) : null,
75
+ };
76
+ }
@@ -0,0 +1,51 @@
1
+ // THE CLOUD'S OWN PERMISSION MODE, put on a cloud run's arguments. Its own file
2
+ // because the Claude harness and the settings both need it, and the settings
3
+ // already import the harnesses.
4
+ import { CLAUDE_PERMISSION_MODES } from '../shared/work-items.mjs';
5
+ import { isCodexMode } from '../shared/codex-modes.mjs';
6
+
7
+ const SKIP_FLAGS = ['--dangerously-skip-permissions'];
8
+
9
+ // Unset, a cloud run does not ask: the sandbox is thrown away after the run,
10
+ // holds no key that can push, and nobody is there to answer a card.
11
+ export const CLOUD_SAME = 'same';
12
+ export const CLOUD_CLAUDE_DEFAULT = 'bypassPermissions';
13
+ export const CLOUD_CODEX_DEFAULT = 'full-access';
14
+
15
+ /**
16
+ * The Claude Code mode a cloud run starts in, or null to keep the flag the run
17
+ * already has: this computer's, or the mode set on the thread itself, which
18
+ * the cloud must not loosen.
19
+ */
20
+ export function cloudClaudeMode(setting, threadMode = null) {
21
+ if (CLAUDE_PERMISSION_MODES.includes(threadMode) || setting === CLOUD_SAME) return null;
22
+ return CLAUDE_PERMISSION_MODES.includes(setting) ? setting : CLOUD_CLAUDE_DEFAULT;
23
+ }
24
+
25
+ /** The Codex mode a cloud run starts in, by the same rule. */
26
+ export function cloudCodexMode(setting, threadMode, here) {
27
+ if (isCodexMode(threadMode)) return threadMode;
28
+ if (setting === CLOUD_SAME) return here;
29
+ return isCodexMode(setting) ? setting : CLOUD_CODEX_DEFAULT;
30
+ }
31
+
32
+ /**
33
+ * Only the permission flag changes, in place, so nothing else moves. Null
34
+ * leaves the run on this computer's mode. With no flag to replace it goes
35
+ * first, where a list flag such as --allowedTools cannot take it as a value.
36
+ */
37
+ export function cloudPermissionArgs(args, mode) {
38
+ if (mode == null) return args;
39
+ if (!CLAUDE_PERMISSION_MODES.includes(mode)) throw new Error(`unknown permission mode: ${mode}`);
40
+ const out = [];
41
+ let at = -1;
42
+ for (let i = 0; i < args.length; i += 1) {
43
+ const arg = String(args[i]);
44
+ const flag = arg === '--permission-mode' || arg.startsWith('--permission-mode=') || SKIP_FLAGS.includes(arg);
45
+ if (!flag) { out.push(args[i]); continue; }
46
+ if (at < 0) at = out.length;
47
+ if (arg === '--permission-mode') i += 1;
48
+ }
49
+ out.splice(at < 0 ? 0 : at, 0, '--permission-mode', mode);
50
+ return out;
51
+ }
@@ -113,6 +113,71 @@ export function codexAccount(home = null, { file = null } = {}) {
113
113
  return { email, name, plan, planNamed: planIsNamed(chatgpt?.chatgpt_plan_type), mode, accountId };
114
114
  }
115
115
 
116
+ /**
117
+ * WHEN THIS LOGIN'S PAID CHATGPT PLAN ENDED, in ms, or null.
118
+ *
119
+ * A lapsed plan reads `chatgpt_plan_type: "free"` and keeps the old
120
+ * subscription's `chatgpt_subscription_active_until`. Only that pair is read as
121
+ * an ending: a paid type with a past date is a token minted before a renewal,
122
+ * and a free login that never paid has no date.
123
+ */
124
+ export function codexPlanEnded(home = null, { file = null, now = Date.now() } = {}) {
125
+ let auth = null;
126
+ try { auth = JSON.parse(fs.readFileSync(file ?? codexAuthFile(home), 'utf8')); } catch { return null; }
127
+ const chatgpt = claims(auth?.tokens?.id_token)?.[AUTH_CLAIM];
128
+ if (String(chatgpt?.chatgpt_plan_type ?? '').trim().toLowerCase() !== 'free') return null;
129
+ const until = Date.parse(chatgpt?.chatgpt_subscription_active_until ?? '');
130
+ return Number.isFinite(until) && until <= now ? until : null;
131
+ }
132
+
133
+ /**
134
+ * WHETHER THE FREE PLAN IS WHY CODEX OFFERS FEW MODELS: `{ endedAt }` when every
135
+ * one of these logins reads Free, with the latest paid plan's end or null, and
136
+ * null when any login is paid or cannot say.
137
+ */
138
+ export function codexFreePlan(homes, { now = Date.now() } = {}) {
139
+ if (!homes.length || !homes.every((h) => codexAccount(h)?.plan === PLANS.free)) return null;
140
+ const ended = homes.map((h) => codexPlanEnded(h, { now })).filter((t) => t != null);
141
+ return { endedAt: ended.length ? Math.max(...ended) : null };
142
+ }
143
+
144
+ /**
145
+ * WHAT KIND OF LOGIN A CODEX HOME HOLDS: 'chatgpt' (a plan), 'apikey' (a key,
146
+ * metered per call), or null when it cannot say.
147
+ *
148
+ * Codex's own reading: `auth_mode` when the file has one, and an older file
149
+ * with no mode is a key when it carries a key and no tokens. A ChatGPT login
150
+ * with a key left beside its tokens is still a ChatGPT login; the mode decides.
151
+ */
152
+ export function codexLoginMode(home = null, { file = null } = {}) {
153
+ let auth = null;
154
+ try { auth = JSON.parse(fs.readFileSync(file ?? codexAuthFile(home), 'utf8')); } catch { return null; }
155
+ if (!auth || typeof auth !== 'object') return null;
156
+ const mode = typeof auth.auth_mode === 'string' ? auth.auth_mode.trim().toLowerCase().replace(/[^a-z]/g, '') : '';
157
+ if (mode) return mode === 'apikey' ? 'apikey' : mode.startsWith('chatgpt') ? 'chatgpt' : mode;
158
+ if (auth.tokens && typeof auth.tokens === 'object') return 'chatgpt';
159
+ return typeof auth.OPENAI_API_KEY === 'string' && auth.OPENAI_API_KEY ? 'apikey' : null;
160
+ }
161
+
162
+ /**
163
+ * WHY A WORKER MAY NOT RUN ON THIS CODEX LOGIN, OR NULL WHEN IT MAY.
164
+ *
165
+ * Workers run on a subscription and never on a key, and the worker scrub
166
+ * keeps OPENAI_API_KEY out of their environment for it. But `codex login
167
+ * --with-api-key` keeps the key in auth.json, which Codex reads for itself,
168
+ * past the scrub: measured 2026-10-15 with codex-cli 0.160.0 on a home laid
169
+ * out that way (the verify skill's `codex-api-key-login` setup), every request
170
+ * a worker sent was signed with the key, and the task was answered as though
171
+ * nothing were wrong. There is no plan in such a home to fall back on, so the
172
+ * run is refused, in words shared/spawn-trouble.mjs files as 'api-key'. The
173
+ * key is never in the sentence.
174
+ */
175
+ export function codexKeyRefusal(home = null, opts = {}) {
176
+ return codexLoginMode(home, opts) === 'apikey'
177
+ ? 'Codex is signed in with an API key, which bills every call to that key. Workers only run on a ChatGPT plan: run codex login in a terminal to sign in with ChatGPT.'
178
+ : null;
179
+ }
180
+
116
181
  /**
117
182
  * A FOLDER FOR THE NEXT CODEX LOGIN, MADE BEFORE ANYTHING IS ASKED TO USE IT.
118
183
  *
@@ -137,6 +202,19 @@ export function makeCodexHome({ home = os.homedir(), mkdir = fs.mkdirSync, exist
137
202
  throw new Error('There are already a lot of Codex folders in your home directory.');
138
203
  }
139
204
 
205
+ /**
206
+ * codex-cli refuses a `CODEX_HOME` that does not exist rather than making it,
207
+ * and exits 1 before anything else happens; an empty folder that exists is
208
+ * fine. Every spawn here names `CODEX_HOME`, so each one goes through this
209
+ * first (tests/codex-never-starts-on-a-home-that-is-not-there).
210
+ *
211
+ * @returns the home it was given, so it can be used inline at a spawn.
212
+ */
213
+ export function ensureCodexHome(home, { mkdir = fs.mkdirSync } = {}) {
214
+ if (home) { try { mkdir(home, { recursive: true }); } catch { /* codex's own message about the folder is the better one */ } }
215
+ return home;
216
+ }
217
+
140
218
  /**
141
219
  * The one line that signs a new account in, for the terminal to run. The home
142
220
  * is spelled out rather than left to the shell, because `~` inside a variable