agentbox-app 0.1.6

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 (302) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +176 -0
  3. package/bin/agentbox.mjs +103 -0
  4. package/briefs/adhd-mode.md +5 -0
  5. package/briefs/message-rules.md +95 -0
  6. package/briefs/worker.md +230 -0
  7. package/briefs/writing-rules.md +336 -0
  8. package/main/account-discovery.mjs +145 -0
  9. package/main/account-tooling.mjs +261 -0
  10. package/main/agent-activity.mjs +56 -0
  11. package/main/agent-files.mjs +422 -0
  12. package/main/agent-schedule.mjs +254 -0
  13. package/main/agent-sessions.mjs +435 -0
  14. package/main/agents.mjs +1095 -0
  15. package/main/analytics.mjs +206 -0
  16. package/main/answer-modes.mjs +206 -0
  17. package/main/approval-prompt-server.mjs +160 -0
  18. package/main/approvals.mjs +487 -0
  19. package/main/artifact-path.mjs +144 -0
  20. package/main/claude-bin.mjs +311 -0
  21. package/main/claude-input.mjs +117 -0
  22. package/main/claude-models.mjs +244 -0
  23. package/main/claude-plan.mjs +148 -0
  24. package/main/claude-usage.mjs +149 -0
  25. package/main/code-change.mjs +383 -0
  26. package/main/code-file.mjs +116 -0
  27. package/main/codex-account.mjs +146 -0
  28. package/main/codex-app-server.mjs +796 -0
  29. package/main/codex-approvals.mjs +712 -0
  30. package/main/codex-bin.mjs +261 -0
  31. package/main/codex-compaction.mjs +49 -0
  32. package/main/codex-models.mjs +194 -0
  33. package/main/codex-session.mjs +797 -0
  34. package/main/codex-threads.mjs +268 -0
  35. package/main/codex-usage-file.mjs +192 -0
  36. package/main/codex-usage.mjs +149 -0
  37. package/main/codex-watch.mjs +35 -0
  38. package/main/codex.mjs +757 -0
  39. package/main/config.mjs +345 -0
  40. package/main/copy-selection.mjs +55 -0
  41. package/main/crash-report.mjs +158 -0
  42. package/main/demo.mjs +254 -0
  43. package/main/dev-window.mjs +103 -0
  44. package/main/doc-file.mjs +99 -0
  45. package/main/doc-scheme.mjs +126 -0
  46. package/main/first-run.mjs +190 -0
  47. package/main/folders.mjs +91 -0
  48. package/main/fresh-user.mjs +120 -0
  49. package/main/git-change.mjs +323 -0
  50. package/main/img-scheme.mjs +90 -0
  51. package/main/instruction-settings.mjs +205 -0
  52. package/main/ipc.mjs +1558 -0
  53. package/main/live-replies.mjs +77 -0
  54. package/main/machine.mjs +113 -0
  55. package/main/main.mjs +972 -0
  56. package/main/menu.mjs +177 -0
  57. package/main/notify.mjs +155 -0
  58. package/main/open-source-file.mjs +8 -0
  59. package/main/project-identity.mjs +149 -0
  60. package/main/provider-commands.mjs +32 -0
  61. package/main/rail-note.mjs +74 -0
  62. package/main/repeats.mjs +261 -0
  63. package/main/row-label.mjs +185 -0
  64. package/main/screen-detail.mjs +44 -0
  65. package/main/serve.mjs +262 -0
  66. package/main/settings.mjs +912 -0
  67. package/main/staleness.mjs +76 -0
  68. package/main/store/chats.mjs +188 -0
  69. package/main/store/home.mjs +383 -0
  70. package/main/store/migrate.mjs +40 -0
  71. package/main/store/paths.mjs +82 -0
  72. package/main/store/pending-writes.mjs +122 -0
  73. package/main/store/project-lock.mjs +200 -0
  74. package/main/store/project.mjs +523 -0
  75. package/main/store/queues.mjs +31 -0
  76. package/main/store/work-items.mjs +299 -0
  77. package/main/store/write-queue.mjs +14 -0
  78. package/main/store-modules.mjs +23 -0
  79. package/main/store.mjs +1269 -0
  80. package/main/supervisor.mjs +6875 -0
  81. package/main/task-commands.mjs +161 -0
  82. package/main/task-folders-offthread.mjs +84 -0
  83. package/main/task-folders-thread.mjs +13 -0
  84. package/main/task-folders.mjs +409 -0
  85. package/main/task-remote-control.mjs +65 -0
  86. package/main/task-terminals.mjs +169 -0
  87. package/main/terminal-access.mjs +6 -0
  88. package/main/updater.mjs +240 -0
  89. package/main/zoom-keys.mjs +54 -0
  90. package/main/zoom-readout.mjs +20 -0
  91. package/mcp/agentbox-mcp.sh +34 -0
  92. package/mcp/core/account.mjs +77 -0
  93. package/mcp/core/chat.mjs +61 -0
  94. package/mcp/core/documents.mjs +238 -0
  95. package/mcp/core/page.mjs +152 -0
  96. package/mcp/core/products.mjs +110 -0
  97. package/mcp/core/skills.mjs +126 -0
  98. package/mcp/core/work.mjs +377 -0
  99. package/mcp/stdio.mjs +70 -0
  100. package/mcp/tools.mjs +257 -0
  101. package/package.json +186 -0
  102. package/renderer/dist/assets/Geist-Variable-Bj2R_7yk.woff2 +0 -0
  103. package/renderer/dist/assets/GeistMono-Variable-Dispecij.woff2 +0 -0
  104. package/renderer/dist/assets/addon-fit-YJmn1quW.js +12 -0
  105. package/renderer/dist/assets/addon-serialize-CsFQTvP_.js +13 -0
  106. package/renderer/dist/assets/cel-dusk-CS5I7kJS.webp +0 -0
  107. package/renderer/dist/assets/cel-dusk-DUslqS6B.webp +0 -0
  108. package/renderer/dist/assets/cel-dusk-Jt_xVGxp.webp +0 -0
  109. package/renderer/dist/assets/cel-dusk-sm-BBkV-tWT.webp +0 -0
  110. package/renderer/dist/assets/frost-haze-CxPSOBEg.webp +0 -0
  111. package/renderer/dist/assets/frost-haze-sm-C_w21qgd.webp +0 -0
  112. package/renderer/dist/assets/gouache-valley-ChSu14-t.webp +0 -0
  113. package/renderer/dist/assets/gouache-valley-d9qasI6k.webp +0 -0
  114. package/renderer/dist/assets/gouache-valley-sm-CdLjT5Ec.webp +0 -0
  115. package/renderer/dist/assets/ice-BC8luINc.webp +0 -0
  116. package/renderer/dist/assets/ice-CP9PrKHo.webp +0 -0
  117. package/renderer/dist/assets/ice-river-D6ziOhLY.webp +0 -0
  118. package/renderer/dist/assets/ice-river-UVM2-JY_.webp +0 -0
  119. package/renderer/dist/assets/ice-river-bErJwYWW.webp +0 -0
  120. package/renderer/dist/assets/ice-river-sm-Bg1U-SIj.webp +0 -0
  121. package/renderer/dist/assets/ice-sm-CbVcg3_D.webp +0 -0
  122. package/renderer/dist/assets/index-CwiDv5bL.css +32 -0
  123. package/renderer/dist/assets/index-D9hCAVPl.js +684 -0
  124. package/renderer/dist/assets/lake-C0WaW-6E.webp +0 -0
  125. package/renderer/dist/assets/lake-CSKjLC5K.webp +0 -0
  126. package/renderer/dist/assets/lake-ETGcOb25.webp +0 -0
  127. package/renderer/dist/assets/lake-harbour-BCQRbrHb.webp +0 -0
  128. package/renderer/dist/assets/lake-harbour-B_wfXTmE.webp +0 -0
  129. package/renderer/dist/assets/lake-harbour-Te1pEvBh.webp +0 -0
  130. package/renderer/dist/assets/lake-harbour-sm-Cme_ShEg.webp +0 -0
  131. package/renderer/dist/assets/lake-sm-CbG72Hbv.webp +0 -0
  132. package/renderer/dist/assets/lino-coast-CUZHcucI.webp +0 -0
  133. package/renderer/dist/assets/lino-coast-DP4J7cgd.webp +0 -0
  134. package/renderer/dist/assets/lino-coast-kOIAffr_.webp +0 -0
  135. package/renderer/dist/assets/lino-coast-sm-C1Mdbgit.webp +0 -0
  136. package/renderer/dist/assets/moon-pines-BskgnnXR.webp +0 -0
  137. package/renderer/dist/assets/moon-pines-CFSIIwt0.webp +0 -0
  138. package/renderer/dist/assets/moon-pines-PNhdOYww.webp +0 -0
  139. package/renderer/dist/assets/moon-pines-sm-C6K1NomK.webp +0 -0
  140. package/renderer/dist/assets/orbit-rings-2XV36Ch3.webp +0 -0
  141. package/renderer/dist/assets/orbit-rings-C4NrcYGQ.webp +0 -0
  142. package/renderer/dist/assets/orbit-rings-DGLk07dT.webp +0 -0
  143. package/renderer/dist/assets/orbit-rings-sm-Dky3YdvL.webp +0 -0
  144. package/renderer/dist/assets/peach-haze-2-C6f7xnWw.webp +0 -0
  145. package/renderer/dist/assets/peach-haze-2-sm-Dj_Ez_Sb.webp +0 -0
  146. package/renderer/dist/assets/pixel-harbour-DQjqMROg.webp +0 -0
  147. package/renderer/dist/assets/pixel-harbour-Dw71H_bc.webp +0 -0
  148. package/renderer/dist/assets/pixel-harbour-kOZfm-GM.webp +0 -0
  149. package/renderer/dist/assets/pixel-harbour-sm-Dturjuky.webp +0 -0
  150. package/renderer/dist/assets/pixel-orbit-C296JVMO.webp +0 -0
  151. package/renderer/dist/assets/pixel-orbit-C4oMRDSx.webp +0 -0
  152. package/renderer/dist/assets/pixel-orbit-CqVmQ4eh.webp +0 -0
  153. package/renderer/dist/assets/pixel-orbit-sm-CHagC_nO.webp +0 -0
  154. package/renderer/dist/assets/pixel-ridge-C6LYu6uE.webp +0 -0
  155. package/renderer/dist/assets/pixel-ridge-CIUCEESs.webp +0 -0
  156. package/renderer/dist/assets/pixel-ridge-sm-BnE0F_w-.webp +0 -0
  157. package/renderer/dist/assets/riso-hills-7heKVtry.webp +0 -0
  158. package/renderer/dist/assets/riso-hills-CIjAL3J6.webp +0 -0
  159. package/renderer/dist/assets/riso-hills-sm-DHsnbCfP.webp +0 -0
  160. package/renderer/dist/assets/slate-haze-BPWdkNsw.webp +0 -0
  161. package/renderer/dist/assets/slate-haze-sm-B0Cb5a2W.webp +0 -0
  162. package/renderer/dist/assets/source-sans-3-cyrillic-ext-wght-normal-DzyfIafT.woff2 +0 -0
  163. package/renderer/dist/assets/source-sans-3-cyrillic-wght-normal-BMDVbyM7.woff2 +0 -0
  164. package/renderer/dist/assets/source-sans-3-greek-ext-wght-normal-BWSLJLk6.woff2 +0 -0
  165. package/renderer/dist/assets/source-sans-3-greek-wght-normal-C9H9m1vD.woff2 +0 -0
  166. package/renderer/dist/assets/source-sans-3-latin-ext-wght-normal-C8iNium2.woff2 +0 -0
  167. package/renderer/dist/assets/source-sans-3-latin-wght-normal-BqRLTx4X.woff2 +0 -0
  168. package/renderer/dist/assets/source-sans-3-vietnamese-wght-normal-C1uRvKPU.woff2 +0 -0
  169. package/renderer/dist/assets/valley-haze-BDHPqoJy.webp +0 -0
  170. package/renderer/dist/assets/valley-haze-sm-CTyjmPAG.webp +0 -0
  171. package/renderer/dist/assets/vector-dunes-DzzWLdKo.webp +0 -0
  172. package/renderer/dist/assets/vector-dunes-Yr9NBQUy.webp +0 -0
  173. package/renderer/dist/assets/vector-dunes-sm-D8MYY6ms.webp +0 -0
  174. package/renderer/dist/assets/watercolour-mist-BCGKN4u4.webp +0 -0
  175. package/renderer/dist/assets/watercolour-mist-BF-jk5MC.webp +0 -0
  176. package/renderer/dist/assets/watercolour-mist-sm-Ds1xzTw9.webp +0 -0
  177. package/renderer/dist/assets/woodblock-sea-B1um67Z9.webp +0 -0
  178. package/renderer/dist/assets/woodblock-sea-DKQdYs32.webp +0 -0
  179. package/renderer/dist/assets/woodblock-sea-sm-BJzNq8xh.webp +0 -0
  180. package/renderer/dist/assets/xterm-BT7tBf8A.js +27 -0
  181. package/renderer/dist/emulator/audio-rules.mjs +74 -0
  182. package/renderer/dist/emulator/data/compression/README.md +11 -0
  183. package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
  184. package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
  185. package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
  186. package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
  187. package/renderer/dist/emulator/data/cores/README.md +22 -0
  188. package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
  189. package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
  190. package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
  191. package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
  192. package/renderer/dist/emulator/data/cores/package.json +65 -0
  193. package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
  194. package/renderer/dist/emulator/data/emulator.css +1614 -0
  195. package/renderer/dist/emulator/data/loader.js +170 -0
  196. package/renderer/dist/emulator/data/localization/README.md +63 -0
  197. package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
  198. package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
  199. package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
  200. package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
  201. package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
  202. package/renderer/dist/emulator/data/localization/en-US.json +339 -0
  203. package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
  204. package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
  205. package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
  206. package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
  207. package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
  208. package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
  209. package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
  210. package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
  211. package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
  212. package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
  213. package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
  214. package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
  215. package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
  216. package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
  217. package/renderer/dist/emulator/data/src/GameManager.js +464 -0
  218. package/renderer/dist/emulator/data/src/compression.js +142 -0
  219. package/renderer/dist/emulator/data/src/emulator.js +6158 -0
  220. package/renderer/dist/emulator/data/src/gamepad.js +167 -0
  221. package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
  222. package/renderer/dist/emulator/data/src/shaders.js +236 -0
  223. package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
  224. package/renderer/dist/emulator/data/src/storage.js +125 -0
  225. package/renderer/dist/emulator/data/version.json +5 -0
  226. package/renderer/dist/emulator/host.js +299 -0
  227. package/renderer/dist/emulator/index.html +28 -0
  228. package/renderer/dist/emulator/provenance.json +6 -0
  229. package/renderer/dist/fixtures/onboard/designs/w-n13/the-first-row.html +1 -0
  230. package/renderer/dist/index.html +13 -0
  231. package/scripts/after-install.mjs +59 -0
  232. package/scripts/approval-server.sh +43 -0
  233. package/scripts/before-publish.mjs +133 -0
  234. package/shared/agent-import.d.mts +13 -0
  235. package/shared/agent-import.mjs +252 -0
  236. package/shared/agents.d.mts +190 -0
  237. package/shared/agents.mjs +937 -0
  238. package/shared/analytics-events.mjs +63 -0
  239. package/shared/answers.d.mts +12 -0
  240. package/shared/answers.mjs +80 -0
  241. package/shared/artifact-keys.d.mts +28 -0
  242. package/shared/artifact-keys.mjs +140 -0
  243. package/shared/bridge-map.mjs +128 -0
  244. package/shared/claude-commands.d.mts +20 -0
  245. package/shared/claude-commands.generated.mjs +149 -0
  246. package/shared/claude-commands.mjs +131 -0
  247. package/shared/claude-models.generated.d.mts +15 -0
  248. package/shared/claude-models.generated.mjs +26 -0
  249. package/shared/claude-usage.d.mts +16 -0
  250. package/shared/claude-usage.mjs +172 -0
  251. package/shared/codex-commands.d.mts +4 -0
  252. package/shared/codex-commands.mjs +17 -0
  253. package/shared/codex-import.d.mts +38 -0
  254. package/shared/codex-import.mjs +232 -0
  255. package/shared/codex-modes.mjs +159 -0
  256. package/shared/codex-usage.mjs +180 -0
  257. package/shared/contracts.mjs +412 -0
  258. package/shared/crash-scrub.mjs +182 -0
  259. package/shared/dashboard.mjs +481 -0
  260. package/shared/dead-run-trace.mjs +127 -0
  261. package/shared/demo-world.mjs +415 -0
  262. package/shared/effort-levels.d.mts +15 -0
  263. package/shared/effort-levels.mjs +76 -0
  264. package/shared/engines.d.mts +62 -0
  265. package/shared/engines.mjs +389 -0
  266. package/shared/first-run-examples.mjs +60 -0
  267. package/shared/first-run-line.mjs +149 -0
  268. package/shared/first-run-practice.d.mts +32 -0
  269. package/shared/first-run-practice.mjs +254 -0
  270. package/shared/first-run-shape.mjs +100 -0
  271. package/shared/fresh-user-home.mjs +189 -0
  272. package/shared/hook-failure.mjs +22 -0
  273. package/shared/install-id.mjs +39 -0
  274. package/shared/instruction-defaults.json +5 -0
  275. package/shared/library-folder.mjs +28 -0
  276. package/shared/notify-rules.mjs +120 -0
  277. package/shared/product-name.d.mts +6 -0
  278. package/shared/product-name.mjs +144 -0
  279. package/shared/project-folder-check.d.mts +22 -0
  280. package/shared/project-folder-check.mjs +152 -0
  281. package/shared/provider-commands.d.mts +8 -0
  282. package/shared/provider-commands.mjs +58 -0
  283. package/shared/rank.d.mts +19 -0
  284. package/shared/rank.mjs +146 -0
  285. package/shared/recovery.mjs +26 -0
  286. package/shared/referenced-files.d.mts +11 -0
  287. package/shared/referenced-files.mjs +122 -0
  288. package/shared/repeats.d.mts +45 -0
  289. package/shared/repeats.mjs +193 -0
  290. package/shared/schemes.mjs +33 -0
  291. package/shared/settings-terminal.d.mts +1 -0
  292. package/shared/settings-terminal.mjs +14 -0
  293. package/shared/side-build.mjs +57 -0
  294. package/shared/spawn-trouble.d.mts +8 -0
  295. package/shared/spawn-trouble.mjs +599 -0
  296. package/shared/terminal-state.d.mts +1 -0
  297. package/shared/terminal-state.mjs +58 -0
  298. package/shared/usage.d.mts +53 -0
  299. package/shared/usage.mjs +319 -0
  300. package/shared/work-items.mjs +561 -0
  301. package/shared/work-lines.mjs +360 -0
  302. package/worker-permissions.json +6 -0
@@ -0,0 +1,415 @@
1
+ // A WHOLE INBOX THAT IS NOBODY'S, so a demo does not have to be her own screen.
2
+ //
3
+ // What is in a demo of Agentbox is the whole product: an inbox of rows an agent
4
+ // filed, a question with options under it, work built and waiting on a yes,
5
+ // something blocked on her. So the demo cannot be an empty store, which is what
6
+ // the new-user row already opens. It is a store with a plausible day in it.
7
+ //
8
+ // THIS FILE IS THE CONTENT AND NOTHING ELSE. No disk, no spawn, no Electron:
9
+ // `main/demo.mjs` writes it out and opens the copy. Two reasons for the split.
10
+ // The content is the half she will want to change, and a file she can be
11
+ // pointed at is easier to change than a paragraph inside a launcher. And the
12
+ // shape of every row here is checked by the same rules the real ones are, which
13
+ // only works while this stays importable by a test with no app around it.
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.
19
+
20
+ import { envName, nameSlug } from './product-name.mjs';
21
+
22
+ /**
23
+ * The mark a demo copy carries, so it can tell what it is. Set in the child's
24
+ * environment by `main/demo.mjs` and read by `shared/install-id.mjs`. */
25
+ export const DEMO_ENV = envName('DEMO');
26
+
27
+ /**
28
+ * A DEMO IS NOT A PERSON AND IT HAS TO SAY SO, for exactly the reason a
29
+ * throwaway first-run home does: a copy that mints a fresh random install id
30
+ * arrives in her own morning brief as a stranger who opened Agentbox once and
31
+ * never came back. A demo copy is opened in front of a room and closed again,
32
+ * which is that pattern on purpose. So every one of them sends under this one
33
+ * fixed id, which names itself as not a person. */
34
+ export const DEMO_INSTALL_ID = `${nameSlug}-demo`;
35
+
36
+ /**
37
+ * Is this copy of Agentbox a demo? The environment mark is the answer; there is
38
+ * no path fallback, because unlike a fresh-user home a demo home is not the
39
+ * only thing that could ever sit under its folder. */
40
+ export function runningAsADemo(env = process.env) {
41
+ return env?.[DEMO_ENV] === '1';
42
+ }
43
+
44
+ /* -------------------------------- the studio ------------------------------ */
45
+
46
+ // THREE PRODUCTS, BECAUSE ONE PRODUCT IS NOT WHAT AGENTBOX IS FOR. The rail, the
47
+ // product filter and the composer's picker all read as furniture with a single
48
+ // project in them. Three is also what her own account looks like from across a
49
+ // room, which means the demo shows the shape of the thing rather than a
50
+ // simplified version of it.
51
+ //
52
+ // NO `repoPath` ON ANY OF THEM, deliberately. A repo path is drawn in the app
53
+ // and checked on disk, so a made-up one either shows a stranger's folder name
54
+ // on the screen or shows a warning that the folder is missing. A product with
55
+ // no code repo registered is an ordinary product (Agentbox itself is one).
56
+ export const DEMO_PRODUCTS = [
57
+ {
58
+ slug: 'kettle',
59
+ name: 'Kettle',
60
+ oneLiner: 'Weeknight dinners, planned and shopped in one tap.',
61
+ },
62
+ {
63
+ slug: 'marlowe',
64
+ name: 'Marlowe',
65
+ oneLiner: 'Reads a contract and says what you just agreed to.',
66
+ },
67
+ {
68
+ slug: 'ferry',
69
+ name: 'Ferry',
70
+ oneLiner: 'Moves a team off Dropbox without losing a folder.',
71
+ },
72
+ ];
73
+
74
+ /* --------------------------------- the day -------------------------------- */
75
+
76
+ // WHAT A GOOD DEMO INBOX HAS IN IT, which is why these nine and not nine
77
+ // others. Every screen worth showing has a row here that lands on it:
78
+ //
79
+ // a question with options the one-key answer, which is the product
80
+ // work built and waiting the "merge it" she should not have to type
81
+ // something blocked on her the app saying it is stuck, out loud
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
84
+ //
85
+ // THE BODIES ARE WRITTEN THE WAY THE WORKER BRIEF SAYS TO WRITE THEM: the ask
86
+ // in bold on the first line, under fifteen words, then the context, then an
87
+ // `## Options` section. That is not decoration here. The list under the reply
88
+ // box is drawn FROM that section, so a body without one demonstrates the app
89
+ // with its best feature switched off.
90
+ //
91
+ // AND NO EM DASHES, on the same standing rule as everything else she reads. A
92
+ // screenshot of this inbox is a screenshot of our own writing.
93
+ const MIN = 60_000;
94
+ const HR = 3_600_000;
95
+
96
+ function lines(now) {
97
+ const rows = [];
98
+
99
+ /* ------------------------------- Kettle -------------------------------- */
100
+
101
+ rows.push({
102
+ product: 'kettle',
103
+ id: 'w-k1c4e0a97b',
104
+ kind: 'review',
105
+ priority: 2,
106
+ at: now - 26 * MIN,
107
+ title: 'Kettle imports 40 of your 42 recipe sites. It is on a branch.',
108
+ body: [
109
+ '**Merge the recipe importer, or hold it for the two sites that fail.**',
110
+ '',
111
+ 'Forty of the forty two import cleanly: ingredients, steps and times, on every one I could check by hand. The two that fail are a site that needs a login and a blog theme that keeps its recipe inside an image.',
112
+ '',
113
+ 'I would merge it and take those two as their own row. Holding the whole importer for a site nobody can read without an account means none of the other forty ship either.',
114
+ '',
115
+ '## Options',
116
+ '1. Merge the importer (recommended)',
117
+ '2. Hold it until all forty two work',
118
+ '3. Merge it and open a row for the two failures',
119
+ ].join('\n'),
120
+ });
121
+
122
+ rows.push({
123
+ product: 'kettle',
124
+ id: 'w-k7b21fd0e3',
125
+ kind: 'question',
126
+ priority: 1,
127
+ at: now - 1.5 * HR,
128
+ title: 'Kettle needs one shop picked before it can order anything.',
129
+ body: [
130
+ "**Say the one with an API, say the bigger one, or say both.**",
131
+ '',
132
+ 'The basket handoff is written and it has nothing to hand off to. One of the two chains has an open API and takes about a day. The other has none, so it means driving their website, which is three days and breaks every time they redesign.',
133
+ '',
134
+ 'I would ship the API one first and add the other once people are actually ordering.',
135
+ '',
136
+ '## Options',
137
+ '1. The one with an API, first (recommended)',
138
+ '2. Both before launch',
139
+ '3. The bigger one first, even though it is three days',
140
+ ].join('\n'),
141
+ });
142
+
143
+ // HER OWN ROW, ANSWERED AND FINISHED. Every demo inbox that is only agent
144
+ // rows shows half the product: the other half is that she can type a
145
+ // sentence at it. The body is written by the founder, so nothing an agent
146
+ // says may overwrite it, and everything the run had to say is in the result.
147
+ rows.push({
148
+ product: 'kettle',
149
+ id: 'w-k9e5a3c118',
150
+ kind: 'directive',
151
+ priority: 2,
152
+ at: now - 5 * HR,
153
+ bodyBy: 'founder',
154
+ title: 'The plan screen only does two people and everyone cooks for a family.',
155
+ body: 'The plan screen only does two people and everyone I have shown it to cooks for a family. Make it scale.',
156
+ status: 'done',
157
+ statusAt: now - 3 * HR,
158
+ result: [
159
+ "Kettle's plan screen now scales from one person to eight, and every quantity on it rounds to something a shop actually sells. A recipe written for two, at four people, asks for 500g of mince rather than 480g.",
160
+ '',
161
+ 'Checked against all twelve recipes in the sample book, not assumed. Two of them round badly at seven people and both say so on the screen rather than quietly being wrong.',
162
+ ].join('\n'),
163
+ });
164
+
165
+ /* ------------------------------- Marlowe ------------------------------- */
166
+
167
+ rows.push({
168
+ product: 'marlowe',
169
+ id: 'w-m2f80b6d54',
170
+ kind: 'question',
171
+ priority: 1,
172
+ at: now - 42 * MIN,
173
+ title: 'Marlowe has two ways to show a risky clause. Pick one.',
174
+ body: [
175
+ '**Say margin note, or say highlight.**',
176
+ '',
177
+ 'A highlight colours the clause where it sits, so you read it in place and nothing on the page moves. A margin note puts a plain sentence beside it saying what the clause actually means, which is more use and pushes the text left on a narrow screen.',
178
+ '',
179
+ 'Six of the eight people in the test read the margin note first, and three of them never noticed the highlight at all. I would take the margin note.',
180
+ '',
181
+ '## Options',
182
+ '1. A margin note beside the clause (recommended)',
183
+ '2. Highlight the clause in place',
184
+ '3. Both, with the note only on a wide screen',
185
+ ].join('\n'),
186
+ });
187
+
188
+ rows.push({
189
+ product: 'marlowe',
190
+ id: 'w-m6a1c93e77',
191
+ kind: 'task',
192
+ priority: 1,
193
+ at: now - 3.2 * HR,
194
+ status: 'blocked',
195
+ title: 'Marlowe cannot take money until the payment account is verified.',
196
+ body: [
197
+ '**Finish the payment account verification. It needs a bank detail only you have.**',
198
+ '',
199
+ 'Checkout is built and it passes against the test keys. The live keys are refused because the account is still unverified, and that is a form nobody but you can fill in.',
200
+ '',
201
+ 'Everything else on the paid tier is done and sitting behind it.',
202
+ ].join('\n'),
203
+ });
204
+
205
+ rows.push({
206
+ product: 'marlowe',
207
+ id: 'w-m4d7e2b901',
208
+ kind: 'review',
209
+ priority: 3,
210
+ at: now - 21 * HR,
211
+ status: 'done',
212
+ statusAt: now - 19 * HR,
213
+ title: 'Marlowe reads a contract and names the clauses that will cost you.',
214
+ result: [
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
+ '',
217
+ 'Ninety three seconds for the longest contract, four seconds for the shortest. Measured on this Mac, not estimated.',
218
+ ].join('\n'),
219
+ });
220
+
221
+ /* -------------------------------- Ferry -------------------------------- */
222
+
223
+ rows.push({
224
+ product: 'ferry',
225
+ id: 'w-4dd4e0a13b',
226
+ kind: 'review',
227
+ priority: 2,
228
+ at: now - 12 * MIN,
229
+ title: "Ferry's launch post is written and goes out the moment you say so.",
230
+ body: [
231
+ '**Post the folder tree announcement, or ask for a different angle.**',
232
+ '',
233
+ 'Ferry now moves a whole team across with the folder tree intact, which is the thing three of the trial teams asked for by name. The post leads with the folder tree, because that is what anybody who has lost one remembers.',
234
+ '',
235
+ 'Nothing is scheduled and nothing is sent. It goes out when you say it does.',
236
+ '',
237
+ '## Options',
238
+ '1. Post it (recommended)',
239
+ '2. Rewrite it around the speed instead',
240
+ '3. Hold it until the second import lands',
241
+ ].join('\n'),
242
+ });
243
+
244
+ rows.push({
245
+ product: 'ferry',
246
+ id: 'w-dfad7295a9',
247
+ kind: 'question',
248
+ priority: 1,
249
+ at: now - 2.4 * HR,
250
+ title: 'Ferry needs a price before the first team can pay for it.',
251
+ body: [
252
+ '**Say per seat, or say one flat price for the whole team.**',
253
+ '',
254
+ 'Per seat is what all three trial teams expected, and it grows as they do. A flat price is easier to say out loud and it is what both competitors do.',
255
+ '',
256
+ 'At the sizes the trial teams actually are, per seat earns more on two of the three and less on the biggest one.',
257
+ '',
258
+ '## Options',
259
+ '1. Per seat (recommended)',
260
+ '2. One flat price per team',
261
+ '3. Per seat, capped at the flat price',
262
+ ].join('\n'),
263
+ });
264
+
265
+ rows.push({
266
+ product: 'ferry',
267
+ id: 'w-ed62d2e9e3',
268
+ kind: 'task',
269
+ priority: 4,
270
+ at: now - 55 * MIN,
271
+ title: 'Ferry loses the date on any file older than 2019.',
272
+ body: [
273
+ '**Nothing to answer. This one is in hand.**',
274
+ '',
275
+ "The destination refuses a modified date before its own epoch on a shared drive, so everything older lands stamped today. The fix is to carry the real date in the file's own description and show that in Ferry's list.",
276
+ '',
277
+ 'It touches nothing anyone has already moved.',
278
+ ].join('\n'),
279
+ });
280
+
281
+ return rows;
282
+ }
283
+
284
+ /* ------------------------------ ledger lines ------------------------------ */
285
+
286
+ // ONE ROW BECOMES THE LINES IT WOULD REALLY HAVE BEEN WRITTEN AS, because the
287
+ // ledger is append-only and everything downstream reads the fold of it. A row
288
+ // that was filed, then answered, then finished is three lines with three
289
+ // timestamps, and the app's own thread view draws them in that order. Writing
290
+ // one fat line instead would give a demo where every row was born finished.
291
+ //
292
+ // `source` is what decides who may overwrite what (shared/work-items.mjs). Her
293
+ // own directive is a founder line, so the demo also shows the case where an
294
+ // agent may not rewrite the title, which is the real rule and not a simplified
295
+ // one.
296
+ export function demoLedgerLines(now = Date.now()) {
297
+ const byProduct = {};
298
+ for (const p of DEMO_PRODUCTS) byProduct[p.slug] = [];
299
+
300
+ for (const row of lines(now)) {
301
+ const at = Math.round(row.at);
302
+ const out = byProduct[row.product];
303
+
304
+ // Filed. `system` writes the row's existence, the way a created item is
305
+ // written today; the body comes on its own line under whoever wrote it.
306
+ out.push({
307
+ id: row.id,
308
+ ts: at,
309
+ source: 'system',
310
+ patch: { title: row.title, status: 'open', kind: row.kind, priority: row.priority, labels: [] },
311
+ });
312
+ if (row.body) {
313
+ out.push({
314
+ id: row.id,
315
+ ts: at + 1,
316
+ source: row.bodyBy === 'founder' ? 'founder' : 'agent',
317
+ patch: { title: row.title, body: row.body },
318
+ });
319
+ }
320
+
321
+ // Finished, with the result on its own later line, which is what makes the
322
+ // inbox row show the result rather than the body (rowSummary, list-rules).
323
+ if (row.status === 'done') {
324
+ out.push({
325
+ id: row.id,
326
+ ts: Math.round(row.statusAt ?? at + HR),
327
+ source: 'agent',
328
+ patch: { status: 'done', result: row.result ?? '' },
329
+ });
330
+ } else if (row.status === 'blocked') {
331
+ out.push({ id: row.id, ts: at + 2, source: 'agent', patch: { status: 'blocked' } });
332
+ }
333
+ }
334
+
335
+ return byProduct;
336
+ }
337
+
338
+ /* --------------------------------- papers --------------------------------- */
339
+
340
+ // A DOCUMENT OR TWO PER PRODUCT, so the documents pane is not empty when she
341
+ // opens it in front of somebody. Short on purpose: these are furniture, and a
342
+ // long invented strategy memo is a thing somebody in the room will start
343
+ // reading instead of watching the demo.
344
+ export const DEMO_DOCS = {
345
+ kettle: [
346
+ {
347
+ path: 'notes.md',
348
+ title: 'Kettle notes',
349
+ text: [
350
+ '# Kettle',
351
+ '',
352
+ 'Weeknight dinners, planned and shopped in one tap.',
353
+ '',
354
+ 'The whole product is one screen: pick a week, get a plan, send the basket.',
355
+ 'Everything else is in service of that screen loading in under a second.',
356
+ '',
357
+ '## What is decided',
358
+ '',
359
+ '- The plan screen scales from one person to eight.',
360
+ '- Quantities round to something a shop actually sells.',
361
+ '- No accounts before the first plan. Nobody signs up to look.',
362
+ ].join('\n'),
363
+ },
364
+ {
365
+ path: 'strategy/who-it-is-for.md',
366
+ title: 'Who Kettle is for',
367
+ text: [
368
+ '# Who it is for',
369
+ '',
370
+ 'People who can cook and do not want to decide.',
371
+ '',
372
+ 'Not people learning to cook, who want a teacher, and not people who already',
373
+ 'plan, who want a spreadsheet. The middle is the whole market and it is large.',
374
+ ].join('\n'),
375
+ },
376
+ ],
377
+ marlowe: [
378
+ {
379
+ path: 'notes.md',
380
+ title: 'Marlowe notes',
381
+ text: [
382
+ '# Marlowe',
383
+ '',
384
+ 'Reads a contract and says what you just agreed to.',
385
+ '',
386
+ 'The test is one sentence: could somebody who has never read a contract',
387
+ 'tell you what happens if they want out in March?',
388
+ '',
389
+ '## Open',
390
+ '',
391
+ '- How a risky clause is shown. Margin note or highlight.',
392
+ '- The payment account is unverified, so nothing can be sold yet.',
393
+ ].join('\n'),
394
+ },
395
+ ],
396
+ ferry: [
397
+ {
398
+ path: 'notes.md',
399
+ title: 'Ferry notes',
400
+ text: [
401
+ '# Ferry',
402
+ '',
403
+ 'Moves a team off Dropbox without losing a folder.',
404
+ '',
405
+ 'The folder tree is the product. Everything else is speed, and speed is',
406
+ 'the second thing anybody asks about.',
407
+ '',
408
+ '## Open',
409
+ '',
410
+ '- A price. Per seat or flat.',
411
+ '- Dates on files older than 2019 land wrong. Fix is written, not shipped.',
412
+ ].join('\n'),
413
+ },
414
+ ],
415
+ };
@@ -0,0 +1,15 @@
1
+ export declare const EFFORT_LEVELS: ReadonlyArray<{
2
+ /** What the app sends Claude Code after `--effort`. */
3
+ id: string;
4
+ /** The word on the strip in the model drawer. */
5
+ label: string;
6
+ }>;
7
+
8
+ /** Whether a value is one of the five words Claude Code takes after `--effort`. */
9
+ export declare function isEffort(value: unknown): value is string;
10
+
11
+ /** Whether a value has the shape of a level at all: one short lowercase word. */
12
+ export declare function isEffortWord(value: unknown): value is string;
13
+
14
+ /** The word on the strip for a level id, on either engine, or null for a non-word. */
15
+ export declare function effortLabel(id: unknown): string | null;
@@ -0,0 +1,76 @@
1
+ // HOW HARD A RUN THINKS, in each engine's own words.
2
+ //
3
+ // Then, of five drawn mechanisms: "A inside the model menu works great." So the
4
+ // levels live in the model drawer on the new task card, under a hairline, and
5
+ // nowhere else. And on 2026-09-14, merging it onto a main that had grown a
6
+ // second engine: "In addition to us literally even supporting codex and those
7
+ // models." So the strip is per engine now.
8
+ //
9
+ // ONE LIST FOR CLAUDE CODE, SHARED BY BOTH SIDES. The renderer draws these
10
+ // words and the main process hands one to `--effort`; a level the card can name
11
+ // that the spawn then refuses (or the other way round) is the drift
12
+ // ../renderer/src/priority.ts exists to prevent, so both read this file.
13
+ //
14
+ // THE IDS ARE THE CLI'S OWN. Measured against Claude Code 2.1.257 on her Mac,
15
+ // 2026-09-01, and again against 2.1.270 on 2026-09-14: `claude --help` prints
16
+ // --effort <level> Effort level for the current session (low, medium, high, xhigh, max)
17
+ // The slash command's usage line adds `ultracode` and `auto`; the flag's does
18
+ // not, so they are not here. `isEffort` is the gate the spawn uses, because a
19
+ // word the CLI does not know would stop the run with a usage error instead of
20
+ // running the task.
21
+ //
22
+ // CODEX'S WORDS ARE NOT IN THIS FILE, ON PURPOSE. They are PER MODEL and they
23
+ // really differ: measured in her `~/.codex/models_cache.json` on 2026-09-14
24
+ // (codex-cli 0.154.0), `gpt-6-astra` and `gpt-5.6-sol` take low, medium, high,
25
+ // xhigh, max and ultra; `gpt-5.6-luna` stops at max; `gpt-5.5` stops at xhigh.
26
+ // So a Codex model's levels are read off the Mac beside its name
27
+ // (main/codex-models.mjs, `levels`) and the strip draws THOSE. What this file
28
+ // holds for Codex is only the shape of a word (`isEffortWord`) and the label
29
+ // for one (`effortLabel`), so the two engines spell "xhigh" the same way on the
30
+ // card.
31
+ //
32
+ // NOTHING SET MEANS NOTHING SENT. There is no row for "the default", for the
33
+ // same reason the model list has none (she killed that row twice on): a level
34
+ // nobody picked is the engine's own choice for the model (Claude Code's, or the
35
+ // `model_reasoning_effort` line in her Codex config.toml), and this app does
36
+ // not know what that is per model, so it does not claim to. The strip draws no
37
+ // lit button until she picks one.
38
+
39
+ export const EFFORT_LEVELS = Object.freeze([
40
+ { id: 'low', label: 'Low' },
41
+ { id: 'medium', label: 'Medium' },
42
+ { id: 'high', label: 'High' },
43
+ // Claude Code spells it `xhigh`. The word on the card is hers to read, and
44
+ // "Extra" beside "High" says it in the room the strip has.
45
+ { id: 'xhigh', label: 'Extra' },
46
+ { id: 'max', label: 'Max' },
47
+ ]);
48
+
49
+ /** Whether a word is one Claude Code will take after `--effort`. */
50
+ export function isEffort(value) {
51
+ return typeof value === 'string' && EFFORT_LEVELS.some((e) => e.id === value);
52
+ }
53
+
54
+ /**
55
+ * Whether a value has the SHAPE of a reasoning level at all: one short
56
+ * lowercase word. This is the gate at the ledger, where the engine's own list
57
+ * is not in hand (Codex's is per model and read off the Mac at spawn). The
58
+ * spawn is the real gate for each engine: `isEffort` for Claude Code, the
59
+ * model's own advertised levels for Codex (`Supervisor#codexEffortRefusal`).
60
+ */
61
+ export function isEffortWord(value) {
62
+ return typeof value === 'string' && /^[a-z][a-z0-9]{0,23}$/.test(value);
63
+ }
64
+
65
+ /**
66
+ * The word on the strip for a level id, for either engine. Claude Code's five
67
+ * keep their labels above; any other id a Codex model advertises (`ultra`) is
68
+ * shown with its first letter up, so the app never invents a name for a level
69
+ * it only learned about from the cache.
70
+ */
71
+ export function effortLabel(id) {
72
+ const known = EFFORT_LEVELS.find((e) => e.id === id);
73
+ if (known) return known.label;
74
+ if (!isEffortWord(id)) return null;
75
+ return id.charAt(0).toUpperCase() + id.slice(1);
76
+ }
@@ -0,0 +1,62 @@
1
+ export interface Engine {
2
+ id: string;
3
+ label: string;
4
+ word: string;
5
+ }
6
+
7
+ export declare const ENGINES: Engine[];
8
+ export declare const ENGINE_IDS: string[];
9
+ export declare const DEFAULT_ENGINE: string;
10
+ // The capability gate. `unique symbol` is what makes TypeScript refuse a
11
+ // boolean or a string here, so an honest caller is stopped at compile time. BE
12
+ // PRECISE ABOUT WHAT THAT BUYS: `any` walks straight through it, as it does
13
+ // through every type in this file, so the declaration is a guard rail and not
14
+ // the guarantee. The guarantee is at runtime and it is unchanged -- the value
15
+ // is a `Symbol` from this module and nothing serialized can be equal to it.
16
+ export declare const ENGINE_CHOICE_ENABLED: unique symbol;
17
+
18
+ /**
19
+ * A row as the fold hands it over: the engine, the model she chose for it, and
20
+ * when the engine field was set. */
21
+ export interface EngineRow {
22
+ engine?: string | null;
23
+ model?: string | null;
24
+ wrote?: Record<string, { ts?: number; source?: string } | undefined>;
25
+ }
26
+ /**
27
+ * The workspace's half. `engineChoice` is `unknown` ON PURPOSE: the whole
28
+ * point of the parse is that anything which is not an ISO date string is off,
29
+ * so a caller must not be able to satisfy the type by writing `true`. */
30
+ export interface EngineWorkspace {
31
+ engine?: string | null;
32
+ engineChoice?: unknown;
33
+ /**
34
+ * When the default above was set, written by the settings screen. `unknown`
35
+ * for the same reason `engineChoice` is. */
36
+ engineAt?: unknown;
37
+ }
38
+ export declare function engineChoiceSince(config?: EngineWorkspace): number | null;
39
+ export declare function engineDefaultSince(config?: EngineWorkspace): number | null;
40
+ export declare function engineDefaultIsStale(config?: EngineWorkspace): boolean;
41
+ export declare function engineChoiceOnRowIsStale(
42
+ item: EngineRow | null | undefined,
43
+ config?: EngineWorkspace,
44
+ ): boolean;
45
+ export declare function isEngine(id: unknown): boolean;
46
+ export declare function engineLabel(id: string | null): string;
47
+ export declare function availableEngines(found?: Record<string, boolean>): Engine[];
48
+ export declare function engineChoiceExists(found?: Record<string, boolean>): boolean;
49
+ export declare function engineFor(
50
+ item: EngineRow | null | undefined,
51
+ where?: {
52
+ config?: EngineWorkspace;
53
+ found?: Record<string, boolean>;
54
+ enabled?: typeof ENGINE_CHOICE_ENABLED;
55
+ },
56
+ ): string;
57
+ export declare function enginePicked(id: string | null): boolean;
58
+ /** The model on this row, or null because it belongs to the other harness. */
59
+ export declare function modelForEngine(
60
+ item: EngineRow | null | undefined,
61
+ engine: string | null,
62
+ ): string | null;