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,136 @@
1
+ // AGENT HOURS: the level, the week, the streak (w-3d0fa23ffb).
2
+ //
3
+ // Pure. A run is `{key, at, start, item, product, engine, seconds, failed,
4
+ // began}`: `at` is when it ended, and a run is counted on the local day it
5
+ // ended. Every run whose agent actually began counts its wall time, failed or
6
+ // not, and overlapping runs each count.
7
+
8
+ export const MASTERY_HOURS = 10_000;
9
+ const MAX_RUN_SECONDS = 24 * 3600;
10
+ const DAY_MS = 86_400_000;
11
+
12
+ // Up to 99, hours grow as level^2.5: level 2 in minutes, 99 at MASTERY_HOURS.
13
+ // Not RuneScape's curve, which spends half its hours on the last seven levels
14
+ // and so reads 82 after one heavy month. Past 99 there is no ceiling and each
15
+ // level costs 5% more than the last, starting from the curve's pace at 99 so
16
+ // no level ever costs less than the one before.
17
+ const RISE = 2.5;
18
+ const PAST = Math.log(1.05);
19
+ const PACE_AT_99 = (MASTERY_HOURS * RISE) / 98;
20
+ // A level starts at its exact hours, not one float step later.
21
+ const EPS = 1e-10;
22
+
23
+ export function hoursForLevel(level) {
24
+ const L = Number(level);
25
+ if (!(L > 1)) return 0;
26
+ if (L <= 99) return MASTERY_HOURS * ((L - 1) / 98) ** RISE;
27
+ return MASTERY_HOURS + (PACE_AT_99 * Math.expm1((L - 99) * PAST)) / PAST;
28
+ }
29
+
30
+ export function levelFor(hours) {
31
+ const h = Number(hours);
32
+ if (!Number.isFinite(h) || h <= 0) return 1;
33
+ if (h < MASTERY_HOURS) return 1 + Math.floor(98 * (h / MASTERY_HOURS) ** (1 / RISE) + EPS);
34
+ return 99 + Math.floor(Math.log1p(((h - MASTERY_HOURS) * PAST) / PACE_AT_99) / PAST + EPS);
35
+ }
36
+
37
+ const pad = (n) => String(n).padStart(2, '0');
38
+ export const localDate = (ms) => { const d = new Date(ms); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; };
39
+ const localMonth = (ms) => localDate(ms).slice(0, 7);
40
+
41
+ function mondayOf(ms) {
42
+ const d = new Date(ms);
43
+ d.setHours(0, 0, 0, 0);
44
+ d.setDate(d.getDate() - ((d.getDay() + 6) % 7));
45
+ return d.getTime();
46
+ }
47
+
48
+ function startOfDay(ms) {
49
+ const d = new Date(ms);
50
+ d.setHours(0, 0, 0, 0);
51
+ return d.getTime();
52
+ }
53
+
54
+ const counts = (r) => r && r.began !== false && Number.isFinite(r.at) && Number(r.seconds) > 0;
55
+
56
+ // One line per key. The line written when the run ended beats one read back
57
+ // from its log, which may have been read while the run was still going.
58
+ export function dedupeRuns(runs) {
59
+ const seen = new Map();
60
+ for (const r of runs ?? []) {
61
+ if (!r || typeof r !== 'object' || typeof r.key !== 'string' || !r.key) continue;
62
+ if (!Number.isFinite(r.at) || !(Number(r.seconds) > 0)) continue;
63
+ const had = seen.get(r.key);
64
+ if (!had || (had.backfilled && !r.backfilled)) seen.set(r.key, r);
65
+ }
66
+ return [...seen.values()];
67
+ }
68
+
69
+ // Days in a row with a run, counted back from today, or from yesterday when
70
+ // today has none yet. `dates` are local YYYY-MM-DD strings, from any number of
71
+ // Macs.
72
+ export function streakOf(dates, now = Date.now()) {
73
+ const have = new Set(dates);
74
+ let cursor = startOfDay(now);
75
+ if (!have.has(localDate(cursor))) cursor -= DAY_MS / 2;
76
+ let n = 0;
77
+ while (have.has(localDate(cursor))) {
78
+ n += 1;
79
+ // Half a day back and then to the start of that day survives a DST change.
80
+ cursor = startOfDay(startOfDay(cursor) - DAY_MS / 2);
81
+ }
82
+ return n;
83
+ }
84
+
85
+ export function summarizeRuns(runs, { now = Date.now() } = {}) {
86
+ const weekStartMs = mondayOf(now);
87
+ const dayStartMs = startOfDay(now);
88
+ let total = 0;
89
+ let week = 0;
90
+ let today = 0;
91
+ const active = new Set();
92
+ const months = new Map();
93
+ const first = new Date(now);
94
+ first.setDate(1);
95
+ for (let i = 11; i >= 0; i -= 1) {
96
+ const d = new Date(first.getFullYear(), first.getMonth() - i, 1);
97
+ months.set(`${d.getFullYear()}-${pad(d.getMonth() + 1)}`, 0);
98
+ }
99
+ for (const r of dedupeRuns(runs)) {
100
+ if (!counts(r) || r.at > now) continue;
101
+ const h = Math.min(Number(r.seconds), MAX_RUN_SECONDS) / 3600;
102
+ total += h;
103
+ if (r.at >= weekStartMs) week += h;
104
+ if (r.at >= dayStartMs) today += h;
105
+ active.add(localDate(r.at));
106
+ const m = localMonth(r.at);
107
+ if (months.has(m)) months.set(m, months.get(m) + h);
108
+ }
109
+ const activeDays = [...active].sort().slice(-60);
110
+ return {
111
+ total,
112
+ week,
113
+ weekStart: localDate(weekStartMs),
114
+ today,
115
+ day: localDate(dayStartMs),
116
+ streak: streakOf(activeDays, now),
117
+ activeDays,
118
+ byMonth: [...months].map(([month, hours]) => ({ month, hours })),
119
+ level: levelFor(total),
120
+ };
121
+ }
122
+
123
+ // One run read back from its trace log, `sessions/<item>/<startedAtMs>.log`:
124
+ // the start is the file's name, the end the `# exited (code) <ISO>` footer,
125
+ // else the file's last write.
126
+ export function runFromTrace({ name, text = '', mtimeMs, item, product }) {
127
+ const m = /^(\d{12,14})\.log$/.exec(String(name ?? ''));
128
+ if (!m) return null;
129
+ const start = Number(m[1]);
130
+ const footer = /# exited \((-?\d+|null)\) (\S+)(?: · [^\n]*)?\s*$/m.exec(String(text));
131
+ const end = footer ? Date.parse(footer[2]) : Number(mtimeMs);
132
+ if (!Number.isFinite(end) || end <= start) return null;
133
+ const seconds = Math.round((end - start) / 1000);
134
+ if (seconds > MAX_RUN_SECONDS) return null;
135
+ return { key: `${item}:${start}`, at: end, start, item, product, engine: null, seconds, failed: footer ? footer[1] !== '0' : null };
136
+ }
@@ -18,7 +18,7 @@
18
18
  /**
19
19
  * The label every one of these rows carries, so a second walk through the
20
20
  * first run can tell which agents already have a row and file nothing twice. */
21
- import { NAME, Name } from '../shared/product-name.mjs';
21
+ import { NAME } from '../shared/product-name.mjs';
22
22
  export const IMPORT_LABEL = 'agent-import';
23
23
 
24
24
  /**
@@ -107,11 +107,11 @@ export function agentsNeedingRows(chosen, existingItems = []) {
107
107
  }
108
108
 
109
109
  /* --------------------------- AND THE THREADS ----------------------------- */
110
- // Of the three things a picked thread could do, this is the plainest one. It
111
- // does NOT resume the conversation, and that is the half these words have to
112
- // carry, because a row named after an earlier conversation is a row anybody
113
- // would assume was carrying it on. So the body says, in plain words, that this
114
- // starts fresh.
110
+ // A picked thread becomes a row that carries the conversation on. It first
111
+ // did not: the body said the reply started fresh, and somebody importing a
112
+ // hundred half-finished sessions was asked to restate every one of them
113
+ // (GitHub #44). The row is still only a row here; the resume happens on the
114
+ // first reply, in `Supervisor#importedThreadFor`.
115
115
  //
116
116
  // Everything else is the agent import's shape, for the same reasons: one row per
117
117
  // thing the user ticked, a label so a second press files nothing twice, and a
@@ -125,6 +125,53 @@ export const THREAD_LABEL = 'thread-import';
125
125
  * which is stable across renames and is what the transcript is filed under. */
126
126
  export const threadLabel = (id) => `thread:${String(id ?? '').trim()}`;
127
127
 
128
+ /**
129
+ * WHETHER A CHAT ENDED WAITING ON YOU, read off the agent's last reply.
130
+ *
131
+ * Waiting means the reply's last line is a question, or a question followed by
132
+ * the choices it offers. `ask` is that question, cut from the front when it is
133
+ * long, because the question is at the end of the line. */
134
+ const ASK_CHARS = 140;
135
+ const SHORT_ASK = 40;
136
+ const CHOICE = /^(\d+[.)]|[-*+]|[a-z][.)])\s+/i;
137
+ const NOT_WAITING = Object.freeze({ waiting: false, ask: '' });
138
+
139
+ function plainLine(line) {
140
+ return String(line)
141
+ .replace(/^(>\s*)+/, '')
142
+ .replace(/^#{1,6}\s+/, '')
143
+ .replace(/[*`]+/g, '')
144
+ // An underscore between two letters is part of a name (account_id).
145
+ .replace(/(?<![A-Za-z0-9])_+|_+(?![A-Za-z0-9])/g, '')
146
+ .replace(/^[("'“‘\s]+/, '')
147
+ .replace(/[)"'”’\s]+$/, '')
148
+ .trim();
149
+ }
150
+
151
+ function shortAsk(line) {
152
+ if (line.length <= ASK_CHARS) return line;
153
+ const sentences = line.split(/(?<=[.!?])\s+/);
154
+ let last = sentences.pop();
155
+ // "Shall I do that?" says nothing without the sentence it follows.
156
+ while (last.length < SHORT_ASK && sentences.length && sentences.at(-1).length + 1 + last.length <= ASK_CHARS) {
157
+ last = `${sentences.pop()} ${last}`;
158
+ }
159
+ if (last.length <= ASK_CHARS) return last;
160
+ const tail = line.slice(-(ASK_CHARS - 1));
161
+ const sp = tail.indexOf(' ');
162
+ return `…${sp > 0 && sp < 40 ? tail.slice(sp + 1) : tail}`;
163
+ }
164
+
165
+ export function howTheChatEnded(said) {
166
+ const lines = String(said ?? '').replace(/\r/g, '').split('\n').map((l) => l.trim()).filter(Boolean);
167
+ let i = lines.length - 1;
168
+ while (i >= 0 && CHOICE.test(lines[i])) i -= 1;
169
+ if (i < 0) return { ...NOT_WAITING };
170
+ const line = plainLine(lines[i].replace(CHOICE, ''));
171
+ if (!line.endsWith('?')) return { ...NOT_WAITING };
172
+ return { waiting: true, ask: shortAsk(line) };
173
+ }
174
+
128
175
  /**
129
176
  * WHEN SHE HAD IT, in the words a person uses about this week.
130
177
  *
@@ -154,37 +201,16 @@ export function startedIn(source) {
154
201
  }
155
202
 
156
203
  /**
157
- * ONE IMPORTED THREAD'S WORDS.
158
- *
159
- * The title is the thread's own, which is the first thing the user typed into
160
- * it, so the row in the inbox is called what they would call it. Ours is not
161
- * added in front of it: a list of rows all opening with the same three words is
162
- * a list that has to be read past to find which is which.
163
- *
164
- * The last paragraph is the honest one. Agentbox read the top of the transcript
165
- * to find this name and nothing else, and the reply starts a fresh session. A
166
- * row that let her believe otherwise would be the same empty promise the agent
167
- * import had before 08-23, in a new place. */
168
- export function threadImportRow(thread, { projectName = null, now = Date.now() } = {}) {
204
+ * ONE IMPORTED THREAD'S WORDS, the chat as it was: the title Claude Code gave
205
+ * it, your first message as the body and the agent's last reply as the
206
+ * result, so the thread opens on the question it ended with. The reply
207
+ * carries the conversation on (`Supervisor#importedThreadFor`). */
208
+ export function threadImportRow(thread) {
169
209
  const title = String(thread?.title || '').trim();
170
- const where = String(thread?.short || thread?.folder || '').trim();
171
- const when = whenWords(thread?.when, now);
172
- const started = [
173
- 'You started this',
174
- startedIn(thread?.source),
175
- when,
176
- where ? `in ${where}` : '',
177
- ].filter(Boolean).join(' ');
178
- const inProject = projectName ? ` here in ${projectName}` : '';
179
- const body = [
180
- '**Say what you want done next in this thread.**',
181
- `${started}.`,
182
- `Reply with the job and ${NAME} runs it${inProject}, answering you here.`,
183
- `${Name} has not read the conversation, only the line above that names it, and replying starts a fresh session rather than carrying the old one on. So say what you need in the reply.`,
184
- ].join('\n\n');
185
210
  return {
186
211
  title,
187
- body,
212
+ body: String(thread?.firstSaid || '').trim() || title,
213
+ result: String(thread?.lastSaid || '').trim(),
188
214
  kind: 'question',
189
215
  priority: 3,
190
216
  labels: [THREAD_LABEL, threadLabel(thread?.id)],