agentbox-app 0.1.7 → 0.1.12

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 (255) hide show
  1. package/README.md +220 -58
  2. package/bin/agentbox.mjs +18 -3
  3. package/briefs/message-rules.md +24 -3
  4. package/briefs/worker.md +63 -49
  5. package/briefs/writing-rules.md +90 -131
  6. package/main/account-discovery.mjs +21 -20
  7. package/main/account-tooling.mjs +11 -11
  8. package/main/agent-activity.mjs +45 -5
  9. package/main/agent-schedule.mjs +14 -14
  10. package/main/agent-sessions.mjs +94 -10
  11. package/main/agent-updates.mjs +116 -0
  12. package/main/agents.mjs +52 -46
  13. package/main/analytics.mjs +27 -0
  14. package/main/approval-prompt-server.mjs +2 -2
  15. package/main/approvals.mjs +14 -5
  16. package/main/artifact-path.mjs +11 -0
  17. package/main/claude-bin.mjs +32 -5
  18. package/main/claude-input.mjs +45 -3
  19. package/main/claude-plan.mjs +25 -13
  20. package/main/code-change.mjs +3 -3
  21. package/main/codex-account.mjs +1 -1
  22. package/main/codex-bin.mjs +18 -0
  23. package/main/codex-launch-env.mjs +31 -0
  24. package/main/codex-memory-gate.mjs +260 -0
  25. package/main/codex-models.mjs +12 -7
  26. package/main/codex-threads.mjs +46 -23
  27. package/main/codex-usage-file.mjs +7 -7
  28. package/main/codex-usage.mjs +4 -4
  29. package/main/codex-watch.mjs +12 -12
  30. package/main/config.mjs +82 -17
  31. package/main/demo.mjs +17 -14
  32. package/main/dev-window.mjs +9 -9
  33. package/main/doc-scheme.mjs +2 -0
  34. package/main/engine-setup.mjs +204 -0
  35. package/main/feedback.mjs +55 -0
  36. package/main/folder-pictures.mjs +48 -0
  37. package/main/frame-scrollbars.mjs +90 -0
  38. package/main/fresh-user.mjs +7 -4
  39. package/main/git-change-offthread.mjs +70 -0
  40. package/main/git-change-thread.mjs +11 -0
  41. package/main/git-change.mjs +9 -3
  42. package/main/img-scheme.mjs +70 -1
  43. package/main/instruction-settings.mjs +134 -60
  44. package/main/ipc.mjs +335 -42
  45. package/main/leftovers.mjs +518 -0
  46. package/main/live-replies.mjs +43 -5
  47. package/main/machine.mjs +48 -1
  48. package/main/main.mjs +138 -15
  49. package/main/memory-gate-history.mjs +254 -0
  50. package/main/memory-gate-server.mjs +461 -0
  51. package/main/memory-gate.mjs +206 -0
  52. package/main/message-priority.mjs +87 -0
  53. package/main/notify.mjs +1 -1
  54. package/main/rail-note.mjs +2 -2
  55. package/main/refresh-agent-models.mjs +21 -0
  56. package/main/repeats.mjs +4 -4
  57. package/main/row-label.mjs +115 -51
  58. package/main/runs-on.mjs +93 -0
  59. package/main/serve.mjs +77 -5
  60. package/main/settings.mjs +245 -26
  61. package/main/ship-queue.mjs +279 -0
  62. package/main/sign-in-files.mjs +39 -0
  63. package/main/source-updater.mjs +298 -0
  64. package/main/store/home.mjs +84 -7
  65. package/main/store/project.mjs +15 -19
  66. package/main/store/work-items.mjs +150 -6
  67. package/main/store.mjs +245 -13
  68. package/main/supervisor.mjs +1634 -417
  69. package/main/task-commands.mjs +8 -10
  70. package/main/task-folders.mjs +94 -16
  71. package/main/task-remote-control.mjs +3 -3
  72. package/main/task-terminals.mjs +14 -2
  73. package/main/team/index.mjs +440 -0
  74. package/main/team/memory-cloud.mjs +247 -0
  75. package/main/team/projects.mjs +162 -0
  76. package/main/team/session.mjs +225 -0
  77. package/main/team/sign-in.mjs +106 -0
  78. package/main/team/supabase-backend.mjs +231 -0
  79. package/main/team/sync.mjs +163 -0
  80. package/main/updater.mjs +15 -5
  81. package/main/write-before-reload.mjs +50 -0
  82. package/mcp/core/page.mjs +10 -12
  83. package/mcp/core/work.mjs +123 -26
  84. package/mcp/tools.mjs +13 -6
  85. package/package.json +21 -9
  86. package/renderer/dist/assets/agentbox-icon-F1oN9nj_.svg +15 -0
  87. package/renderer/dist/assets/index-ChKsQkRw.js +1169 -0
  88. package/renderer/dist/assets/index-ClBXnq3B.css +32 -0
  89. package/renderer/dist/index.html +2 -2
  90. package/scripts/after-install.mjs +14 -0
  91. package/scripts/approval-server.sh +7 -2
  92. package/scripts/memory-gate-hook.sh +59 -0
  93. package/shared/agent-import.mjs +39 -16
  94. package/shared/agents.d.mts +26 -8
  95. package/shared/agents.mjs +100 -108
  96. package/shared/analytics-events.mjs +19 -2
  97. package/shared/answers.mjs +12 -0
  98. package/shared/bridge-map.d.mts +12 -0
  99. package/shared/bridge-map.mjs +38 -1
  100. package/shared/claude-commands.generated.mjs +2 -2
  101. package/shared/claude-models.generated.mjs +6 -6
  102. package/shared/claude-usage.mjs +6 -6
  103. package/shared/codex-import.mjs +7 -7
  104. package/shared/codex-modes.d.mts +50 -0
  105. package/shared/codex-modes.mjs +1 -1
  106. package/shared/codex-usage.mjs +7 -8
  107. package/shared/demo-world.mjs +6 -6
  108. package/shared/engines.mjs +27 -10
  109. package/shared/feedback.d.mts +14 -0
  110. package/shared/feedback.mjs +44 -0
  111. package/shared/first-run-examples.mjs +4 -4
  112. package/shared/first-run-practice.d.mts +16 -0
  113. package/shared/first-run-practice.mjs +176 -70
  114. package/shared/fresh-user-home.mjs +34 -4
  115. package/shared/instruction-defaults.json +2 -2
  116. package/shared/product-name.mjs +4 -2
  117. package/shared/project-folder-check.mjs +9 -3
  118. package/shared/rank.d.mts +1 -0
  119. package/shared/rank.mjs +22 -0
  120. package/shared/referenced-files.mjs +1 -1
  121. package/shared/runs-on.d.mts +20 -0
  122. package/shared/runs-on.mjs +79 -0
  123. package/shared/schemes.d.mts +11 -0
  124. package/shared/schemes.mjs +1 -1
  125. package/shared/shortcuts.d.mts +41 -0
  126. package/shared/shortcuts.mjs +236 -0
  127. package/shared/spawn-trouble.d.mts +2 -0
  128. package/shared/spawn-trouble.mjs +40 -19
  129. package/shared/team-rules.d.mts +17 -0
  130. package/shared/team-rules.mjs +230 -0
  131. package/shared/team-status.d.mts +11 -0
  132. package/shared/team-status.mjs +90 -0
  133. package/shared/terminal-links.d.mts +2 -0
  134. package/shared/terminal-links.mjs +55 -0
  135. package/shared/terminal-state.d.mts +1 -0
  136. package/shared/terminal-state.mjs +16 -0
  137. package/shared/thread-cards.d.mts +19 -0
  138. package/shared/thread-cards.mjs +254 -0
  139. package/shared/usage.d.mts +1 -1
  140. package/shared/usage.mjs +6 -7
  141. package/shared/work-items.mjs +258 -28
  142. package/shared/work-lines.mjs +393 -16
  143. package/worker-permissions.json +1 -1
  144. package/renderer/dist/assets/cel-dusk-CS5I7kJS.webp +0 -0
  145. package/renderer/dist/assets/cel-dusk-DUslqS6B.webp +0 -0
  146. package/renderer/dist/assets/cel-dusk-Jt_xVGxp.webp +0 -0
  147. package/renderer/dist/assets/cel-dusk-sm-BBkV-tWT.webp +0 -0
  148. package/renderer/dist/assets/frost-haze-CxPSOBEg.webp +0 -0
  149. package/renderer/dist/assets/frost-haze-sm-C_w21qgd.webp +0 -0
  150. package/renderer/dist/assets/ice-BC8luINc.webp +0 -0
  151. package/renderer/dist/assets/ice-CP9PrKHo.webp +0 -0
  152. package/renderer/dist/assets/ice-river-D6ziOhLY.webp +0 -0
  153. package/renderer/dist/assets/ice-river-UVM2-JY_.webp +0 -0
  154. package/renderer/dist/assets/ice-river-bErJwYWW.webp +0 -0
  155. package/renderer/dist/assets/ice-river-sm-Bg1U-SIj.webp +0 -0
  156. package/renderer/dist/assets/ice-sm-CbVcg3_D.webp +0 -0
  157. package/renderer/dist/assets/index-B7hhfHrT.js +0 -684
  158. package/renderer/dist/assets/index-CwiDv5bL.css +0 -32
  159. package/renderer/dist/assets/lake-C0WaW-6E.webp +0 -0
  160. package/renderer/dist/assets/lake-CSKjLC5K.webp +0 -0
  161. package/renderer/dist/assets/lake-ETGcOb25.webp +0 -0
  162. package/renderer/dist/assets/lake-harbour-BCQRbrHb.webp +0 -0
  163. package/renderer/dist/assets/lake-harbour-B_wfXTmE.webp +0 -0
  164. package/renderer/dist/assets/lake-harbour-Te1pEvBh.webp +0 -0
  165. package/renderer/dist/assets/lake-harbour-sm-Cme_ShEg.webp +0 -0
  166. package/renderer/dist/assets/lake-sm-CbG72Hbv.webp +0 -0
  167. package/renderer/dist/assets/lino-coast-CUZHcucI.webp +0 -0
  168. package/renderer/dist/assets/lino-coast-DP4J7cgd.webp +0 -0
  169. package/renderer/dist/assets/lino-coast-kOIAffr_.webp +0 -0
  170. package/renderer/dist/assets/lino-coast-sm-C1Mdbgit.webp +0 -0
  171. package/renderer/dist/assets/moon-pines-BskgnnXR.webp +0 -0
  172. package/renderer/dist/assets/moon-pines-CFSIIwt0.webp +0 -0
  173. package/renderer/dist/assets/moon-pines-PNhdOYww.webp +0 -0
  174. package/renderer/dist/assets/moon-pines-sm-C6K1NomK.webp +0 -0
  175. package/renderer/dist/assets/orbit-rings-2XV36Ch3.webp +0 -0
  176. package/renderer/dist/assets/orbit-rings-C4NrcYGQ.webp +0 -0
  177. package/renderer/dist/assets/orbit-rings-DGLk07dT.webp +0 -0
  178. package/renderer/dist/assets/orbit-rings-sm-Dky3YdvL.webp +0 -0
  179. package/renderer/dist/assets/peach-haze-2-C6f7xnWw.webp +0 -0
  180. package/renderer/dist/assets/peach-haze-2-sm-Dj_Ez_Sb.webp +0 -0
  181. package/renderer/dist/assets/pixel-harbour-DQjqMROg.webp +0 -0
  182. package/renderer/dist/assets/pixel-harbour-Dw71H_bc.webp +0 -0
  183. package/renderer/dist/assets/pixel-harbour-kOZfm-GM.webp +0 -0
  184. package/renderer/dist/assets/pixel-harbour-sm-Dturjuky.webp +0 -0
  185. package/renderer/dist/assets/pixel-orbit-C296JVMO.webp +0 -0
  186. package/renderer/dist/assets/pixel-orbit-C4oMRDSx.webp +0 -0
  187. package/renderer/dist/assets/pixel-orbit-CqVmQ4eh.webp +0 -0
  188. package/renderer/dist/assets/pixel-orbit-sm-CHagC_nO.webp +0 -0
  189. package/renderer/dist/assets/pixel-ridge-C6LYu6uE.webp +0 -0
  190. package/renderer/dist/assets/pixel-ridge-CIUCEESs.webp +0 -0
  191. package/renderer/dist/assets/pixel-ridge-sm-BnE0F_w-.webp +0 -0
  192. package/renderer/dist/assets/riso-hills-7heKVtry.webp +0 -0
  193. package/renderer/dist/assets/riso-hills-CIjAL3J6.webp +0 -0
  194. package/renderer/dist/assets/riso-hills-sm-DHsnbCfP.webp +0 -0
  195. package/renderer/dist/assets/slate-haze-BPWdkNsw.webp +0 -0
  196. package/renderer/dist/assets/slate-haze-sm-B0Cb5a2W.webp +0 -0
  197. package/renderer/dist/assets/valley-haze-BDHPqoJy.webp +0 -0
  198. package/renderer/dist/assets/valley-haze-sm-CTyjmPAG.webp +0 -0
  199. package/renderer/dist/assets/vector-dunes-DzzWLdKo.webp +0 -0
  200. package/renderer/dist/assets/vector-dunes-Yr9NBQUy.webp +0 -0
  201. package/renderer/dist/assets/vector-dunes-sm-D8MYY6ms.webp +0 -0
  202. package/renderer/dist/assets/watercolour-mist-BCGKN4u4.webp +0 -0
  203. package/renderer/dist/assets/watercolour-mist-BF-jk5MC.webp +0 -0
  204. package/renderer/dist/assets/watercolour-mist-sm-Ds1xzTw9.webp +0 -0
  205. package/renderer/dist/assets/woodblock-sea-B1um67Z9.webp +0 -0
  206. package/renderer/dist/assets/woodblock-sea-DKQdYs32.webp +0 -0
  207. package/renderer/dist/assets/woodblock-sea-sm-BJzNq8xh.webp +0 -0
  208. package/renderer/dist/emulator/audio-rules.mjs +0 -74
  209. package/renderer/dist/emulator/data/compression/README.md +0 -11
  210. package/renderer/dist/emulator/data/compression/extract7z.js +0 -1
  211. package/renderer/dist/emulator/data/compression/extractzip.js +0 -1
  212. package/renderer/dist/emulator/data/compression/libunrar.js +0 -519
  213. package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
  214. package/renderer/dist/emulator/data/cores/README.md +0 -22
  215. package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
  216. package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
  217. package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
  218. package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
  219. package/renderer/dist/emulator/data/cores/package.json +0 -65
  220. package/renderer/dist/emulator/data/cores/reports/mgba.json +0 -1
  221. package/renderer/dist/emulator/data/emulator.css +0 -1614
  222. package/renderer/dist/emulator/data/loader.js +0 -170
  223. package/renderer/dist/emulator/data/localization/README.md +0 -63
  224. package/renderer/dist/emulator/data/localization/af-FR.json +0 -339
  225. package/renderer/dist/emulator/data/localization/ar-AR.json +0 -301
  226. package/renderer/dist/emulator/data/localization/ben-BEN.json +0 -301
  227. package/renderer/dist/emulator/data/localization/de-GER.json +0 -301
  228. package/renderer/dist/emulator/data/localization/el-GR.json +0 -301
  229. package/renderer/dist/emulator/data/localization/en-US.json +0 -339
  230. package/renderer/dist/emulator/data/localization/es-ES.json +0 -310
  231. package/renderer/dist/emulator/data/localization/fa-AF.json +0 -310
  232. package/renderer/dist/emulator/data/localization/hi-HI.json +0 -301
  233. package/renderer/dist/emulator/data/localization/it-IT.json +0 -302
  234. package/renderer/dist/emulator/data/localization/ja-JA.json +0 -301
  235. package/renderer/dist/emulator/data/localization/jv-JV.json +0 -301
  236. package/renderer/dist/emulator/data/localization/ko-KO.json +0 -336
  237. package/renderer/dist/emulator/data/localization/pt-BR.json +0 -361
  238. package/renderer/dist/emulator/data/localization/retroarch.json +0 -617
  239. package/renderer/dist/emulator/data/localization/ro-RO.json +0 -310
  240. package/renderer/dist/emulator/data/localization/ru-RU.json +0 -301
  241. package/renderer/dist/emulator/data/localization/tr-TR.json +0 -310
  242. package/renderer/dist/emulator/data/localization/vi-VN.json +0 -302
  243. package/renderer/dist/emulator/data/localization/zh-CN.json +0 -351
  244. package/renderer/dist/emulator/data/src/GameManager.js +0 -464
  245. package/renderer/dist/emulator/data/src/compression.js +0 -142
  246. package/renderer/dist/emulator/data/src/emulator.js +0 -6158
  247. package/renderer/dist/emulator/data/src/gamepad.js +0 -167
  248. package/renderer/dist/emulator/data/src/nipplejs.js +0 -1
  249. package/renderer/dist/emulator/data/src/shaders.js +0 -236
  250. package/renderer/dist/emulator/data/src/socket.io.min.js +0 -7
  251. package/renderer/dist/emulator/data/src/storage.js +0 -125
  252. package/renderer/dist/emulator/data/version.json +0 -5
  253. package/renderer/dist/emulator/host.js +0 -299
  254. package/renderer/dist/emulator/index.html +0 -28
  255. package/renderer/dist/emulator/provenance.json +0 -6
@@ -11,16 +11,16 @@
11
11
  // number typed into a file of ours would look authoritative while being a
12
12
  // guess. Running an old model by mistake is the failure being avoided.
13
13
 
14
- export const READ_FROM = "2.1.283";
15
- export const READ_AT = "2026-09-29";
14
+ export const READ_FROM = "2.1.293";
15
+ export const READ_AT = "2026-10-08";
16
16
 
17
17
  export const CLAUDE_MODELS = [
18
18
  {"alias":"opus","id":"claude-opus-5-5","label":"Opus 5.5","defaultLevel":"medium"},
19
19
  {"alias":"claude-opus-5","id":"claude-opus-5","label":"Opus 5","defaultLevel":"high"},
20
- {"alias":"sonnet","id":"claude-sonnet-5","label":"Sonnet 5","defaultLevel":"high"},
21
- {"alias":"claude-sonnet-4-6","id":"claude-sonnet-4-6","label":"Sonnet 4.6","defaultLevel":null},
22
- {"alias":"haiku","id":"claude-haiku-4-5","label":"Haiku 4.5","defaultLevel":null},
23
- {"alias":"claude-3-5-haiku","id":"claude-3-5-haiku","label":"Haiku 3.5","defaultLevel":null},
20
+ {"alias":"sonnet","id":"claude-sonnet-5-5","label":"Sonnet 5.5","defaultLevel":"medium"},
21
+ {"alias":"claude-sonnet-5","id":"claude-sonnet-5","label":"Sonnet 5","defaultLevel":"high"},
22
+ {"alias":"haiku","id":"claude-haiku-5-5","label":"Haiku 5.5","defaultLevel":"medium"},
23
+ {"alias":"claude-haiku-4-5","id":"claude-haiku-4-5","label":"Haiku 4.5","defaultLevel":null},
24
24
  {"alias":"fable","id":"claude-fable-5-1","label":"Fable 5.1","defaultLevel":"high"},
25
25
  {"alias":"claude-fable-5","id":"claude-fable-5","label":"Fable 5","defaultLevel":"high"},
26
26
  ];
@@ -28,11 +28,11 @@ import { instantIn, localZone, sessionLimit, untilReset, wallIn } from './usage.
28
28
  // { span, qualifier, name, percent, resetsAt|null, resetsText, resetsOn, zone }
29
29
 
30
30
  const LINE = /^Current\s+(session|week)(?:\s*\(([^)]+)\))?\s*:\s*(\d+)%\s*used(?:\s*·\s*resets\s+(.+?))?\s*$/i;
31
- // THE MINUTES ARE OPTIONAL, AND THAT IS WHY HER CORNER WENT QUIET. Claude Code
32
- // drops the ":00" when a reset lands on the hour, so the stamp on her Mac read
33
- // "Sep 1 at 4pm" and this refused it. Refusing it took the countdown out of the
34
- // pill AND out of the tooltip, which left the wordless "41%" she photographed
35
- // and could not read. It is one hour in every five.
31
+ // THE MINUTES ARE OPTIONAL, AND THAT IS WHY THE CORNER WENT QUIET. Claude Code
32
+ // drops the ":00" when a reset lands on the hour, so a stamp reads "Sep 1 at
33
+ // 4pm" and this refused it. Refusing it took the countdown out of the pill AND
34
+ // out of the tooltip, which left a wordless percentage nobody could read. It
35
+ // is one hour in every five.
36
36
  //
37
37
  // THE AM/PM HALF STAYS REQUIRED. An hour on its own is ambiguous by twelve
38
38
  // hours, and the standing rule in this file is to refuse rather than guess.
@@ -90,7 +90,7 @@ export function resetAt(text, now = Date.now(), zoneNow = localZone()) {
90
90
  }
91
91
 
92
92
  /**
93
- * WHAT EACH LIMIT IS CALLED, IN HER WORDS. One copy, because the sentence the
93
+ * WHAT EACH LIMIT IS CALLED, IN PLAIN WORDS. One copy, because the sentence the
94
94
  * corner reads aloud and the panel's rows have to agree word for word: they are
95
95
  * on the screen at the same moment, one of them read aloud and one of them read.
96
96
  *
@@ -13,9 +13,9 @@
13
13
  // now, so opening the row draws the strip and a 1 or a 2 answers it exactly as
14
14
  // Y and N do on the row.
15
15
  //
16
- // NOTHING IS RESUMED. She chose that on 08-29 for Claude Code threads and it
17
- // holds here: a yes makes a row she can READ, with her prompt and Codex's
18
- // last message, and replies still happen in Codex.
16
+ // NOTHING IS RESUMED. That is the rule for Claude Code threads and it
17
+ // holds here: a yes makes a row the user can READ, with their prompt and
18
+ // Codex's last message, and replies still happen in Codex.
19
19
  //
20
20
  // A NO NEVER LOSES ONE.So a no closes the row under its own label, Closed shows
21
21
  // it under "Not imported" with an Import key, and the ⌘K import card lists it
@@ -29,7 +29,7 @@ import { Name } from '../shared/product-name.mjs';
29
29
  export const CODEX_IMPORT_LABEL = 'codex-import';
30
30
  /** On the row once it is hers to read. */
31
31
  export const CODEX_MIRROR_LABEL = 'codex';
32
- /** On a row she said not now to. */
32
+ /** On a row the user said not now to. */
33
33
  export const NOT_IMPORTED_LABEL = 'not-imported';
34
34
  /** And which conversation, on all three. Codex's own thread id. */
35
35
  export const codexLabel = (id) => `codex:${String(id ?? '').trim()}`;
@@ -68,7 +68,7 @@ export function whenPhrase(when, now = Date.now()) {
68
68
  * to a notes file in its worktree.
69
69
  *
70
70
  * So: pictures are left out. A link to the open web stays a link, because it
71
- * works from anywhere (her own cofounder.co link does). A link to this Mac, a
71
+ * works from anywhere. A link to this Mac, a
72
72
  * file path or a localhost address, keeps its words and loses its target, and
73
73
  * a line that was nothing but such a link goes entirely, since "Open the
74
74
  * interactive preview." with nothing to open is the confusing part. */
@@ -173,7 +173,7 @@ export function codexImportRow(thread, { projectName, now = Date.now() } = {}) {
173
173
  }
174
174
 
175
175
  /**
176
- * THE ROW ONCE SHE SAID YES. Her prompt is the body and Codex's last message
176
+ * THE ROW ONCE THE USER SAID YES. Their prompt is the body and Codex's last message
177
177
  * is the result, so the thread reads You, then Codex, the way it did over
178
178
  * there. The result carries the one line that says where replies go. */
179
179
  export function codexMirrorPatch(thread) {
@@ -201,7 +201,7 @@ export function importChoice(answer) {
201
201
 
202
202
  /**
203
203
  * WHICH PROJECT A CONVERSATION BELONGS TO: the one whose folder it ran in,
204
- * her rule from 08-25. Null when nobody points at that folder. */
204
+ * the same rule every import follows. Null when nobody points at that folder. */
205
205
  export function productsForThread(thread, products = []) {
206
206
  const key = String(thread?.folder ?? '').replace(/\/+$/, '');
207
207
  if (!key) return [];
@@ -0,0 +1,50 @@
1
+ // Types for shared/codex-modes.mjs, for the window side.
2
+ //
3
+ // The three ids are `CodexModeId` in renderer/src/types.ts, which is where that
4
+ // union already lives; this file states the shape around them rather than a
5
+ // second copy of the list. renderer/src/codex-modes.ts is the screen's own
6
+ // narrowed view of the same table and reads these through it.
7
+ import type { CodexModeId } from '../renderer/src/types';
8
+
9
+ /**
10
+ * THE PAIR A MODE REALLY IS. `sandbox` and `approvalPolicy` go straight into
11
+ * Codex's `thread/start`; `approvalsReviewer` is set on `auto` alone, which is
12
+ * the only row with asks left to answer. They are plain strings rather than
13
+ * unions on purpose: the authority for what Codex accepts is Codex's own
14
+ * schema, quoted in the .mjs, and a union here would be a third copy of it
15
+ * that nothing checks.
16
+ */
17
+ export interface CodexMode {
18
+ label: string;
19
+ sandbox: string;
20
+ approvalPolicy: string;
21
+ approvalsReviewer?: string;
22
+ what: string;
23
+ }
24
+
25
+ export declare const CODEX_MODE_ORDER: CodexModeId[];
26
+ export declare const CODEX_DEFAULT_MODE: CodexModeId;
27
+ export declare const CODEX_MODES: Record<CodexModeId, CodexMode>;
28
+ export declare const CODEX_MODE_COMMAND: Record<CodexModeId, string>;
29
+ export declare const CODEX_MODE_HINT: Record<CodexModeId, string>;
30
+ export declare const CODEX_MODE_STATUS: Record<CodexModeId, string>;
31
+
32
+ export declare function isCodexMode(word: unknown): boolean;
33
+ /** The pair, falling back to the default's pair when the word is not one of ours. */
34
+ export declare function codexPosture(mode: string | null | undefined): {
35
+ sandbox: string;
36
+ approvalPolicy: string;
37
+ approvalsReviewer?: string;
38
+ };
39
+ export declare function codexModeWords(mode: string | null | undefined): string;
40
+ /**
41
+ * The rows the menu draws for what has been typed. `null` is the way back to
42
+ * the project's own setting and is a row like the others.
43
+ */
44
+ export declare function codexMenuRowsFor(
45
+ query: string | null,
46
+ set?: unknown,
47
+ ): (CodexModeId | null)[];
48
+ export declare function nextCodexMode(mode: CodexModeId): CodexModeId;
49
+ /** The mode a typed `/word ` means, or null when it means nothing. */
50
+ export declare function exactCodexMode(word: string | null | undefined): CodexModeId | null;
@@ -71,7 +71,7 @@ export const CODEX_MODES = {
71
71
  label: 'Full access',
72
72
  sandbox: 'danger-full-access',
73
73
  approvalPolicy: 'never',
74
- what: 'Anything, anywhere on this Mac, without asking.',
74
+ what: 'Anything, anywhere on this computer, without asking.',
75
75
  },
76
76
  };
77
77
 
@@ -58,17 +58,16 @@
58
58
  // ---------------------------------------------------------------------------
59
59
  // AND `primary` IS NOT ALWAYS THE FIVE HOUR WINDOW. The block above read one
60
60
  // scratch account on 2026-09-05 and took the pairing it happened to see as the
61
- // shape. Measured again on HER OWN account, 2026-09-18, across the 1,140
62
- // rate-limit records in her twenty-five most recent Codex sessions: EVERY ONE of
63
- // them had `secondary: null` and a `primary` whose window was 10080 minutes. Not
64
- // one five hour window exists on her plan.
61
+ // shape. On some Codex plans the rate-limit records look different: EVERY ONE
62
+ // of them has `secondary: null` and a `primary` whose window is 10080 minutes.
63
+ // Not one five hour window exists on such a plan.
65
64
  //
66
- // So the old table would have drawn her SEVEN DAY limit under the words "This
67
- // session", which is a confident wrong number about her own subscription, and
68
- // the corner would have counted a week down as though it were an afternoon.
65
+ // So the old table would have drawn a SEVEN DAY limit under the words "This
66
+ // session", which is a confident wrong number about the user's subscription,
67
+ // and the corner would have counted a week down as though it were an afternoon.
69
68
  //
70
69
  // THE WINDOW'S OWN LENGTH DECIDES WHAT IT IS CALLED, and the slot it arrived in
71
- // decides nothing. 300 minutes is her session, 10080 is her week, and an account
70
+ // decides nothing. 300 minutes is a session, 10080 is a week, and an account
72
71
  // reporting only one of them gets only that one row. The slot order is still the
73
72
  // tie-breaker when a payload names no duration at all, because that is the one
74
73
  // case where there is nothing better to go on.
@@ -12,10 +12,10 @@
12
12
  // shape of every row here is checked by the same rules the real ones are, which
13
13
  // only works while this stays importable by a test with no app around it.
14
14
  //
15
- // NOTHING HERE IS HERS AND NOTHING HERE IS REAL. Three invented products, one
16
- // invented week of work. No path on this Mac, no name of anyone she knows, no
17
- // product she actually ships. That is the whole point of the row: what goes on
18
- // the screen in front of a room is invented, and her own inbox stays shut.
15
+ // NOTHING HERE IS THE USER'S AND NOTHING HERE IS REAL. Three invented products,
16
+ // one invented week of work. No path on this Mac, no name of anyone real, no
17
+ // product anyone actually ships. That is the whole point of the row: what goes
18
+ // on the screen in front of a room is invented, and the real inbox stays shut.
19
19
 
20
20
  import { envName, nameSlug } from './product-name.mjs';
21
21
 
@@ -80,7 +80,7 @@ export const DEMO_PRODUCTS = [
80
80
  // work built and waiting the "merge it" she should not have to type
81
81
  // something blocked on her the app saying it is stuck, out loud
82
82
  // a finished row a result, and what the inbox looks like empty
83
- // one row she wrote herself her own words on a card, answered
83
+ // one row the user wrote the user's own words on a card, answered
84
84
  //
85
85
  // THE BODIES ARE WRITTEN THE WAY THE WORKER BRIEF SAYS TO WRITE THEM: the ask
86
86
  // in bold on the first line, under fifteen words, then the context, then an
@@ -214,7 +214,7 @@ function lines(now) {
214
214
  result: [
215
215
  'The clause summariser is merged and running. It read all ten contracts in the test folder and named every termination, auto renewal and liability clause in all ten, with two false positives on the longest one.',
216
216
  '',
217
- 'Ninety three seconds for the longest contract, four seconds for the shortest. Measured on this Mac, not estimated.',
217
+ 'Ninety three seconds for the longest contract, four seconds for the shortest. Measured on this computer, not estimated.',
218
218
  ].join('\n'),
219
219
  });
220
220
 
@@ -25,15 +25,15 @@
25
25
  // :494). Nothing has ever been written to clear it on purpose, so the honest
26
26
  // statement is "probably still there", not "always will be".
27
27
  //
28
- // HOW MANY SUCH ROWS SHE ACTUALLY HAS IS AN OPEN QUESTION, AND IT IS NOT ZERO
29
- // UNTIL SOMEBODY LOOKS. It could not be measured from the machine this was
30
- // written on: there is no ~/Zero there at all. On her Mac it is one command,
28
+ // HOW MANY SUCH ROWS A STORE ACTUALLY HAS IS AN OPEN QUESTION, AND IT IS NOT
29
+ // ZERO UNTIL SOMEBODY LOOKS. It could not be measured from the machine this was
30
+ // written on: there is no ~/Zero there at all. On a real store it is one command,
31
31
  // `grep -rho '"engine":"[a-z]*"' ~/Zero | sort | uniq -c`, and the answer
32
32
  // belongs in this paragraph when someone runs it. The gate below costs nothing
33
33
  // if the count turns out to be zero, and is the whole story if it is not.
34
34
  //
35
35
  // So the 08-25 resolver, restored unchanged, would have been a silent reversal
36
- // of her decision: those rows would start choosing Codex again the moment this
36
+ // of the decision to withhold Codex: those rows would start choosing Codex again the moment this
37
37
  // module landed, with no picker, nothing in Settings, and no approval path
38
38
  // built. The resolver is therefore CLOSED unless the caller hands it
39
39
  // `ENGINE_CHOICE_ENABLED` below. A caller that passes nothing gets Claude
@@ -143,7 +143,7 @@ export const ENGINE_CHOICE_ENABLED = Symbol('the second engine is wired up in th
143
143
  * in the app's own state rather than in her file: a fresh install, a state file
144
144
  * she deleted, or a second Mac would each invent a different moment, and the
145
145
  * one thing this value must be is the same on every machine reading the same
146
- * store. A date she typed is a fact about her decision; a date we noticed is a
146
+ * store. A date the user typed is a fact about their decision; a date we noticed is a
147
147
  * fact about a process.
148
148
  *
149
149
  * THE WORKSPACE DEFAULT (`config.engine`) IS TIME-SCOPED TOO, SINCE 2026-09-05,
@@ -210,7 +210,7 @@ export function engineDefaultSince(config = {}) {
210
210
  * gate withholds — the same rule `engineChoiceOnRowIsStale` follows below, and
211
211
  * for the same reason: discarding it could only ever move work TO the second
212
212
  * engine, which is the one direction the gate exists to prevent. False when
213
- * there is no stamp, which is a default she typed beside the moment itself.
213
+ * there is no stamp, which is a default typed beside the moment itself.
214
214
  */
215
215
  export function engineDefaultIsStale(config = {}) {
216
216
  const since = engineChoiceSince(config);
@@ -271,9 +271,9 @@ export function engineLabel(id) {
271
271
  * `found` is a map of id to boolean. An engine whose binary is not on the
272
272
  * machine is not offered, because a picker that offers something that cannot
273
273
  * run is a picker that hands her a session that dies on arrival. Claude Code
274
- * is always in the list even when it was not found: the app already refuses to
275
- * open an inbox without it and says so in its own words (main/claude-bin.mjs),
276
- * and a second sentence here saying the same thing differently is noise.
274
+ * is in the list even when it was not found, UNLESS Codex was: a Mac with only
275
+ * Codex is offered only Codex (`homeEngine` below). With neither, the app
276
+ * refuses to open an inbox and says so in its own words.
277
277
  *
278
278
  * UNGATED, AND THE INVARIANT IS THEREFORE SCOPED TO ROUTING. Say it plainly,
279
279
  * because the file must not claim more than the code delivers: this function,
@@ -296,7 +296,21 @@ export function engineLabel(id) {
296
296
  * holds the caller list exactly, so the next one has to argue for itself here.
297
297
  */
298
298
  export function availableEngines(found = {}) {
299
- return ENGINES.filter((e) => e.id === DEFAULT_ENGINE || found[e.id]);
299
+ const home = homeEngine(found);
300
+ return ENGINES.filter((e) => e.id === home || (e.id !== DEFAULT_ENGINE && found[e.id]));
301
+ }
302
+
303
+ /**
304
+ * WHAT RUNS WHEN NOBODY HAS CHOSEN, ON THIS MAC. Claude Code, unless this Mac
305
+ * has said for certain that Claude Code is not here and Codex is. The app needs
306
+ * one of the two, not both, so a Mac with only Codex runs everything on Codex.
307
+ *
308
+ * `found.claude` has to be an explicit `false`. A caller that never asked
309
+ * (every call written before this, and every test config) leaves it undefined,
310
+ * and that reads as Claude Code being here, which is what it always meant.
311
+ */
312
+ export function homeEngine(found = {}) {
313
+ return found.claude === false && found.codex ? 'codex' : DEFAULT_ENGINE;
300
314
  }
301
315
 
302
316
  /** Whether there is a choice to make at all. One engine is not a choice. */
@@ -334,6 +348,9 @@ export function engineChoiceExists(found = {}) {
334
348
  * token FROM that same value, so in her app the two cannot come apart.
335
349
  */
336
350
  export function engineFor(item, { config = {}, found = {}, enabled } = {}) {
351
+ // ONE ENGINE ON THE MAC IS NOT A CHOICE, so the gate has nothing to guard.
352
+ // Without Claude Code, a row marked `claude` would only die on arrival.
353
+ if (homeEngine(found) !== DEFAULT_ENGINE) return homeEngine(found);
337
354
  if (enabled !== ENGINE_CHOICE_ENABLED) return DEFAULT_ENGINE;
338
355
  const wanted = isEngine(item?.engine) && !engineChoiceOnRowIsStale(item, config) ? item.engine : null;
339
356
  const fallback = isEngine(config?.engine) && !engineDefaultIsStale(config) ? config.engine : DEFAULT_ENGINE;
@@ -0,0 +1,14 @@
1
+ // Types for shared/feedback.mjs, which is plain ESM so the Feedback card and
2
+ // the main process read one copy of the limits.
3
+
4
+ export const FEEDBACK_LIMITS: Readonly<{ text: number; files: number; bytes: number }>;
5
+
6
+ export function totalBytes(files?: { size?: number }[]): number;
7
+
8
+ export function checkFeedback(p?: { text?: string; files?: { size?: number }[] }): { ok: true } | { ok: false; reason: string };
9
+
10
+ export function feedbackPayload(p?: {
11
+ text?: string;
12
+ files?: { name: string; type: string; data: string }[];
13
+ app?: { version?: string; os?: string };
14
+ }): { text: string; files: { name: string; type: string; data: string }[]; app: { version: string; os: string } };
@@ -0,0 +1,44 @@
1
+ // WHAT A PIECE OF FEEDBACK MAY BE, in one place, read by the card before it
2
+ // lets you press Send and by the main process again before anything leaves the
3
+ // machine (w-1b574413db). The server function checks the same limits a third
4
+ // time, because it is the only one of the three a stranger can reach directly.
5
+ //
6
+ // Words, files, or both, and never nothing. The size limit keeps one request
7
+ // well inside what the mail provider takes as attachments.
8
+
9
+ export const FEEDBACK_LIMITS = Object.freeze({
10
+ text: 20_000,
11
+ files: 10,
12
+ bytes: 8 * 1024 * 1024,
13
+ });
14
+
15
+ export function totalBytes(files = []) {
16
+ return files.reduce((sum, f) => sum + (Number.isFinite(f?.size) ? f.size : 0), 0);
17
+ }
18
+
19
+ // { ok: true } or { ok: false, reason } where the reason is a sentence the
20
+ // card can show as it is.
21
+ export function checkFeedback({ text = '', files = [] } = {}) {
22
+ const words = String(text ?? '').trim();
23
+ const list = Array.isArray(files) ? files : [];
24
+ if (!words && list.length === 0) return { ok: false, reason: 'Write something or attach a file first.' };
25
+ if (words.length > FEEDBACK_LIMITS.text) {
26
+ return { ok: false, reason: `Keep it under ${FEEDBACK_LIMITS.text.toLocaleString('en-US')} characters.` };
27
+ }
28
+ if (list.length > FEEDBACK_LIMITS.files) return { ok: false, reason: `Attach up to ${FEEDBACK_LIMITS.files} files.` };
29
+ if (totalBytes(list) > FEEDBACK_LIMITS.bytes) {
30
+ return { ok: false, reason: `Files can add up to ${FEEDBACK_LIMITS.bytes / 1024 / 1024} MB.` };
31
+ }
32
+ return { ok: true };
33
+ }
34
+
35
+ // Exactly what travels: the words trimmed, each file as name, type and its
36
+ // base64, and the app's version so a report can be matched to a release.
37
+ // Nothing else the card happened to hold rides along.
38
+ export function feedbackPayload({ text = '', files = [], app = {} } = {}) {
39
+ return {
40
+ text: String(text ?? '').trim(),
41
+ files: (files ?? []).map(({ name, type, data }) => ({ name: String(name ?? 'file'), type: String(type || 'application/octet-stream'), data: String(data ?? '') })),
42
+ app: { version: String(app.version ?? ''), os: String(app.os ?? '') },
43
+ };
44
+ }
@@ -21,19 +21,19 @@
21
21
  export const EXAMPLES = [
22
22
  {
23
23
  kind: 'question',
24
- title: 'Example: which of these two names should the project use?',
25
- result: 'Example. Both are free. Say either one and I will rename everything to match.',
24
+ title: 'Example: which of these two dates should the offsite use?',
25
+ result: 'Example. Both are free. Say either one and I will book it.',
26
26
  agoMs: 4 * 60_000,
27
27
  },
28
28
  {
29
29
  kind: 'task',
30
- title: 'Example: the sign in page is built and the tests are green.',
30
+ title: 'Example: the meeting notes are summarised and shared.',
31
31
  result: 'Example. Nothing here needs you. Close it and it is gone.',
32
32
  agoMs: 11 * 60_000,
33
33
  },
34
34
  {
35
35
  kind: 'review',
36
- title: 'Example: I took the smaller library, say if you disagree.',
36
+ title: 'Example: I picked the cheaper venue, say if you disagree.',
37
37
  result: 'Example. This is what an agent looks like when it made a call without you.',
38
38
  agoMs: 26 * 60_000,
39
39
  },
@@ -7,10 +7,26 @@ export const PRACTICE_SLUG: string;
7
7
  export const PRACTICE_FLAG: string;
8
8
  export const PRACTICE_TASK: { title: string; body: string };
9
9
  export const PRACTICE_ANSWER: string;
10
+ export const PRACTICE_REPLY: string;
11
+ export const PRACTICE_REPLY_ANSWER: string;
10
12
  export const PRACTICE_TASK_TRACE: string[];
11
13
  /** The practice project's sidebar note, written to its `pinned.md` and drawn
12
14
  * beside the third introduction slab. */
13
15
  export const PRACTICE_NOTE: string;
16
+ /** The rest of the practice team's work, drawn from the tab tour onward so
17
+ * the list and the board are not empty. `state` is the column it lands in. */
18
+ export const PRACTICE_BACKDROP: {
19
+ state: 'needs' | 'working' | 'scheduled';
20
+ kind: string;
21
+ title: string;
22
+ body: string;
23
+ trace: string[];
24
+ /** Only on the `needs` rows: what the agent handed back. */
25
+ result?: string;
26
+ agoMs: number;
27
+ /** Only on the `scheduled` row: how long after staging it is set to run. */
28
+ inMs?: number;
29
+ }[];
14
30
  export const PRACTICE_ROWS: {
15
31
  kind: string;
16
32
  title: string;