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
package/shared/rank.mjs CHANGED
@@ -69,19 +69,15 @@ export function placeScore(order, slug, direct) {
69
69
  /**
70
70
  * The number an item carries into that score.
71
71
  *
72
- * An AGENT'S tag counts for nothing. Agents used to set this themselves, on a
73
- * rule in the worker brief that told them 7 meant blocking; of the 442 items
74
- * agents filed, 127 came in at 7 or higher, which is not an order, it is
75
- * noise. She asked for the ordering to be the app's job, the rule is out of
76
- * the brief, and this is the half that also covers the items already sitting
77
- * in the store carrying an agent's 8.
78
- *
79
- * Everything else counts, and the test for "hers" is deliberately wide: her
80
- * compose box writes the number as 'system' rather than 'founder' (the split
81
- * in store.composeItem is words-versus-lifecycle, not authority), and a repeat
82
- * rule's number arrives the same way. Reading only 'founder' here would have
83
- * thrown away the Urgent tag on the very row she complained about
84
- * (tests/urgent-does-not-wait-behind-medium.test.mjs).
72
+ * A LEVEL COUNTS WHOEVER SET IT (w-5ac9c9006d, 2026-10-16). An agent's tag
73
+ * used to count for nothing, because agents once tagged 127 of 442 rows at 7
74
+ * or higher on an old brief rule. That made every level an agent wrote a lie
75
+ * on screen, and her answer was to stop discarding them: "If agent-determined
76
+ * priorities are ignored, then it seems like the whole prioritization system
77
+ * is ignored. If an agent sets something and I'm like, 'Oh that's the wrong
78
+ * priority,' I'll fix it. We should also always treat priorities as the
79
+ * source of truth, regardless of where they came from." Her fix is a new
80
+ * level, and the fold keeps hers over an agent's.
85
81
  *
86
82
  * Nought means untagged, not bottom. It is the seed the fold starts an item
87
83
  * at and the default her compose sends when she picks no number, and her own
@@ -93,11 +89,29 @@ export const DEFAULT_PRIORITY = 5;
93
89
 
94
90
  export function itemPriority(item) {
95
91
  if (!item) return DEFAULT_PRIORITY;
96
- if (item.wrote?.priority?.source === 'agent') return DEFAULT_PRIORITY;
97
92
  const n = item.priority;
98
93
  return Number.isFinite(n) && n > 0 ? n : DEFAULT_PRIORITY;
99
94
  }
100
95
 
96
+ /**
97
+ * THE LEVEL A THREAD SHOWS IS THE LEVEL IT RUNS AT (w-5ac9c9006d, 2026-10-15).
98
+ *
99
+ * Every screen drew the raw stored number while the fleet ran `itemPriority`,
100
+ * so a thread an agent filed untagged read Low and ran as Medium, and an
101
+ * agent's own 8 read High and ran as Medium. In Progress then showed three
102
+ * "Low" threads running while a "High" and a "Medium" waited. An agent's level
103
+ * now counts (above); an untagged row reads Medium here as it runs.
104
+ *
105
+ * A CONVERSATION keeps the number it carries: its level is the message
106
+ * sorter's (main/message-priority.mjs), written as an agent on purpose, and a
107
+ * conversation never spawns, so there is no running order for it to disagree
108
+ * with. `conversation` is the caller's to say, since only it knows the product.
109
+ */
110
+ export function shownPriority(item, conversation = false) {
111
+ if (conversation) return item?.priority ?? null;
112
+ return itemPriority(item);
113
+ }
114
+
101
115
  /**
102
116
  * The top of her own scale, and the only level that INTERRUPTS.
103
117
  *
@@ -110,12 +124,11 @@ export function itemPriority(item) {
110
124
  export const URGENT_PRIORITY = 9;
111
125
 
112
126
  /**
113
- * Is this HER urgent row?
127
+ * Is this row Urgent?
114
128
  *
115
- * itemPriority already throws away an agent's number, so this cannot be
116
- * reached by a worker tagging its own proposal a nine. That matters more here
117
- * than anywhere else in this file: everything downstream of this predicate
118
- * takes a running session away from something else.
129
+ * Whoever set it, since 2026-10-16 (itemPriority). Everything downstream of
130
+ * this predicate takes a running session away from something else, so an
131
+ * agent's nine interrupts like hers; a wrong one is hers to lower.
119
132
  */
120
133
  export function isUrgent(item) {
121
134
  return itemPriority(item) >= URGENT_PRIORITY;
@@ -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,10 +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
4
  }
5
5
 
6
6
  /** The word for the plan behind an engine, or null for one we cannot name. */
7
7
  export declare function runsOnName(engine?: string | null): string | null;
8
8
 
9
- /** The one line the walk says on a Mac that was already set up. Null for null. */
10
- 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,11 +1,11 @@
1
- // THE ONE LINE SAYING IT FOUND AN ACCOUNT ALREADY ON THIS MAC.
1
+ // THE ONE LINE SAYING IT FOUND AN ACCOUNT ALREADY ON THIS COMPUTER.
2
2
  //
3
3
  // From a session with a new user, 2026-10-07, in her words: a new user came in,
4
4
  // started running an agent, and was "like, 'Oh my God, where are the tokens
5
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
@@ -43,13 +43,16 @@ export function runsOnName(engine) {
43
43
  }
44
44
 
45
45
  /**
46
- * 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.
47
47
  *
48
48
  * It replaces silence, not a question: somebody with no coding agent set up
49
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.
50
53
  */
51
- export function foundOnThisMac({ engine = null } = {}) {
54
+ export function foundOnThisComputer({ engine = null } = {}) {
52
55
  const name = runsOnName(engine);
53
56
  if (!name) return null;
54
- 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.`;
55
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());
@@ -93,6 +93,7 @@
93
93
  // touching it, so the row names no sections at all.
94
94
 
95
95
  import { NAME } from './product-name.mjs';
96
+ import { chordOf, everyChord } from './key-caps.mjs';
96
97
 
97
98
  /** @type {import('./shortcuts.d.mts').ShortcutGroup[]} */
98
99
  export const SHORTCUTS = [
@@ -199,10 +200,11 @@ export const SHORTCUTS = [
199
200
  // (meta||ctrl) && 'k' → the palette, above every other branch, and
200
201
  // forwarded from inside an open file by main.mjs as well.
201
202
  { keys: ['⌘K', 'Ctrl+K'], join: 'or', what: `The command bar: everything ${NAME} can do, by name.` },
202
- // 'n' in both branches → setModal('compose'). C does the same and is
203
- // left off: N is the default (w-fb9051e597).
203
+ // 'n' in both branches → setModal('compose').
204
204
  // "Threads", not tasks, since w-ec62ab6b38.
205
205
  { keys: ['N'], what: 'Start a new thread.' },
206
+ // cloudChord in the chords → toggleCloudRef: the cloud on or off.
207
+ { keys: ['⌘C'], what: 'Turn the cloud on or off, when no text is selected.' },
206
208
  // '/' above the focused branch → openSearch
207
209
  { keys: ['/'], what: 'Search your threads.' },
208
210
  // '\\' above the focused branch → togglePanel
@@ -225,18 +227,24 @@ export function everyKey(groups = SHORTCUTS) {
225
227
  // moves here has moved for every agent on its next run. The closing line is
226
228
  // the point: an agent's own saved memory is read before anything we hand it,
227
229
  // and on 2026-10-04 one said "press S" three days after S stopped scheduling.
230
+ // And in the glyphs of the keyboard in front of them: an agent telling
231
+ // somebody on Linux to "press ⌘K" names a key they do not have, which is the
232
+ // same mistake as a stale memory.
228
233
  /** The keys as one block of plain text, for an agent's system prompt. */
229
- export function keysForAgents(groups = SHORTCUTS) {
234
+ export function keysForAgents(groups = SHORTCUTS, { mac = true } = {}) {
230
235
  const lines = [
231
236
  `The keys in ${NAME}, the app this person reads you in, as they are today.`,
232
237
  'When you tell them to press something, use these. Keys have moved before,',
233
238
  'so a key you remember from an earlier session, a memory or a note that',
234
- 'disagrees with this list is out of date. Anything without a key is in ⌘K',
235
- 'by name.',
239
+ 'disagrees with this list is out of date. Anything without a key is in',
240
+ `${chordOf('⌘K', mac)} by name.`,
236
241
  ];
237
242
  for (const g of groups) {
238
243
  lines.push('', `${g.label}:`);
239
- for (const k of g.keys) lines.push(`- ${k.keys.join(k.join ? ` ${k.join} ` : ' ')}: ${k.what}`);
244
+ for (const k of g.keys) {
245
+ const keys = everyChord(k.keys, mac);
246
+ lines.push(`- ${keys.join(k.join && keys.length > 1 ? ` ${k.join} ` : ' ')}: ${k.what}`);
247
+ }
240
248
  }
241
249
  return lines.join('\n');
242
250
  }
@@ -8,3 +8,7 @@
8
8
  export function strandedTitle(count: number): string;
9
9
  /** The line in a thread where a run stopped, in our words; `raw` is the tool's own refusal. */
10
10
  export function runStoppedLine(raw: string): string;
11
+ /** True when `text` is the app's own word that a run got nowhere. */
12
+ export function saysARunGotNowhere(text: string | null | undefined): boolean;
13
+ /** What one account's tile says when it cannot run; `dir` is its config folder, for the sign-in command. */
14
+ export function accountSentence(cause: string, dir?: string | null): string;