agentbox-app 0.1.6 → 0.1.11

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 (244) hide show
  1. package/README.md +209 -55
  2. package/bin/agentbox.mjs +6 -0
  3. package/briefs/message-rules.md +13 -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 +9 -10
  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 +114 -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/artifact-path.mjs +11 -0
  16. package/main/claude-bin.mjs +31 -5
  17. package/main/claude-input.mjs +45 -3
  18. package/main/claude-plan.mjs +11 -11
  19. package/main/code-change.mjs +3 -3
  20. package/main/codex-account.mjs +1 -1
  21. package/main/codex-bin.mjs +17 -0
  22. package/main/codex-memory-gate.mjs +260 -0
  23. package/main/codex-models.mjs +12 -7
  24. package/main/codex-threads.mjs +46 -23
  25. package/main/codex-usage-file.mjs +7 -7
  26. package/main/codex-usage.mjs +4 -4
  27. package/main/codex-watch.mjs +12 -12
  28. package/main/config.mjs +68 -8
  29. package/main/dev-window.mjs +9 -9
  30. package/main/doc-scheme.mjs +2 -0
  31. package/main/engine-setup.mjs +193 -0
  32. package/main/feedback.mjs +55 -0
  33. package/main/folder-pictures.mjs +48 -0
  34. package/main/frame-scrollbars.mjs +90 -0
  35. package/main/fresh-user.mjs +6 -3
  36. package/main/git-change-offthread.mjs +70 -0
  37. package/main/git-change-thread.mjs +11 -0
  38. package/main/git-change.mjs +1 -1
  39. package/main/img-scheme.mjs +70 -1
  40. package/main/instruction-settings.mjs +92 -54
  41. package/main/ipc.mjs +316 -42
  42. package/main/leftovers.mjs +518 -0
  43. package/main/live-replies.mjs +43 -5
  44. package/main/machine.mjs +47 -0
  45. package/main/main.mjs +119 -15
  46. package/main/memory-gate-history.mjs +254 -0
  47. package/main/memory-gate-server.mjs +461 -0
  48. package/main/memory-gate.mjs +206 -0
  49. package/main/message-priority.mjs +87 -0
  50. package/main/rail-note.mjs +2 -2
  51. package/main/refresh-agent-models.mjs +20 -0
  52. package/main/repeats.mjs +4 -4
  53. package/main/row-label.mjs +114 -51
  54. package/main/serve.mjs +67 -5
  55. package/main/settings.mjs +223 -26
  56. package/main/ship-queue.mjs +279 -0
  57. package/main/sign-in-files.mjs +39 -0
  58. package/main/source-updater.mjs +298 -0
  59. package/main/store/home.mjs +47 -2
  60. package/main/store/work-items.mjs +150 -6
  61. package/main/store.mjs +235 -9
  62. package/main/supervisor.mjs +1444 -406
  63. package/main/task-commands.mjs +8 -10
  64. package/main/task-folders.mjs +74 -13
  65. package/main/task-remote-control.mjs +3 -3
  66. package/main/task-terminals.mjs +2 -1
  67. package/main/team/index.mjs +440 -0
  68. package/main/team/memory-cloud.mjs +247 -0
  69. package/main/team/projects.mjs +162 -0
  70. package/main/team/session.mjs +225 -0
  71. package/main/team/sign-in.mjs +106 -0
  72. package/main/team/supabase-backend.mjs +231 -0
  73. package/main/team/sync.mjs +163 -0
  74. package/main/updater.mjs +15 -5
  75. package/main/write-before-reload.mjs +50 -0
  76. package/mcp/core/page.mjs +9 -11
  77. package/mcp/core/work.mjs +91 -25
  78. package/mcp/tools.mjs +12 -6
  79. package/package.json +21 -9
  80. package/renderer/dist/assets/agentbox-icon-F1oN9nj_.svg +15 -0
  81. package/renderer/dist/assets/index-B-2Az952.css +32 -0
  82. package/renderer/dist/assets/index-D6qncgai.js +1168 -0
  83. package/renderer/dist/index.html +2 -2
  84. package/scripts/memory-gate-hook.sh +59 -0
  85. package/shared/agent-import.mjs +38 -15
  86. package/shared/agents.d.mts +26 -8
  87. package/shared/agents.mjs +100 -108
  88. package/shared/analytics-events.mjs +19 -2
  89. package/shared/answers.mjs +12 -0
  90. package/shared/bridge-map.d.mts +12 -0
  91. package/shared/bridge-map.mjs +37 -1
  92. package/shared/claude-commands.generated.mjs +2 -2
  93. package/shared/claude-models.generated.mjs +4 -4
  94. package/shared/claude-usage.mjs +6 -6
  95. package/shared/codex-import.mjs +7 -7
  96. package/shared/codex-modes.d.mts +50 -0
  97. package/shared/codex-usage.mjs +7 -8
  98. package/shared/demo-world.mjs +5 -5
  99. package/shared/engines.mjs +27 -10
  100. package/shared/feedback.d.mts +14 -0
  101. package/shared/feedback.mjs +44 -0
  102. package/shared/first-run-examples.mjs +4 -4
  103. package/shared/first-run-practice.d.mts +16 -0
  104. package/shared/first-run-practice.mjs +176 -70
  105. package/shared/fresh-user-home.mjs +22 -3
  106. package/shared/instruction-defaults.json +2 -2
  107. package/shared/product-name.mjs +4 -2
  108. package/shared/project-folder-check.mjs +6 -0
  109. package/shared/rank.d.mts +1 -0
  110. package/shared/rank.mjs +22 -0
  111. package/shared/referenced-files.mjs +1 -1
  112. package/shared/schemes.d.mts +11 -0
  113. package/shared/schemes.mjs +1 -1
  114. package/shared/shortcuts.d.mts +41 -0
  115. package/shared/shortcuts.mjs +236 -0
  116. package/shared/spawn-trouble.d.mts +2 -0
  117. package/shared/spawn-trouble.mjs +39 -18
  118. package/shared/team-rules.d.mts +17 -0
  119. package/shared/team-rules.mjs +230 -0
  120. package/shared/team-status.d.mts +11 -0
  121. package/shared/team-status.mjs +90 -0
  122. package/shared/terminal-links.d.mts +2 -0
  123. package/shared/terminal-links.mjs +55 -0
  124. package/shared/terminal-state.d.mts +1 -0
  125. package/shared/terminal-state.mjs +16 -0
  126. package/shared/thread-cards.d.mts +19 -0
  127. package/shared/thread-cards.mjs +254 -0
  128. package/shared/usage.d.mts +1 -1
  129. package/shared/usage.mjs +6 -7
  130. package/shared/work-items.mjs +255 -27
  131. package/shared/work-lines.mjs +393 -16
  132. package/worker-permissions.json +1 -1
  133. package/renderer/dist/assets/cel-dusk-CS5I7kJS.webp +0 -0
  134. package/renderer/dist/assets/cel-dusk-DUslqS6B.webp +0 -0
  135. package/renderer/dist/assets/cel-dusk-Jt_xVGxp.webp +0 -0
  136. package/renderer/dist/assets/cel-dusk-sm-BBkV-tWT.webp +0 -0
  137. package/renderer/dist/assets/frost-haze-CxPSOBEg.webp +0 -0
  138. package/renderer/dist/assets/frost-haze-sm-C_w21qgd.webp +0 -0
  139. package/renderer/dist/assets/ice-BC8luINc.webp +0 -0
  140. package/renderer/dist/assets/ice-CP9PrKHo.webp +0 -0
  141. package/renderer/dist/assets/ice-river-D6ziOhLY.webp +0 -0
  142. package/renderer/dist/assets/ice-river-UVM2-JY_.webp +0 -0
  143. package/renderer/dist/assets/ice-river-bErJwYWW.webp +0 -0
  144. package/renderer/dist/assets/ice-river-sm-Bg1U-SIj.webp +0 -0
  145. package/renderer/dist/assets/ice-sm-CbVcg3_D.webp +0 -0
  146. package/renderer/dist/assets/index-CwiDv5bL.css +0 -32
  147. package/renderer/dist/assets/index-D9hCAVPl.js +0 -684
  148. package/renderer/dist/assets/lake-C0WaW-6E.webp +0 -0
  149. package/renderer/dist/assets/lake-CSKjLC5K.webp +0 -0
  150. package/renderer/dist/assets/lake-ETGcOb25.webp +0 -0
  151. package/renderer/dist/assets/lake-harbour-BCQRbrHb.webp +0 -0
  152. package/renderer/dist/assets/lake-harbour-B_wfXTmE.webp +0 -0
  153. package/renderer/dist/assets/lake-harbour-Te1pEvBh.webp +0 -0
  154. package/renderer/dist/assets/lake-harbour-sm-Cme_ShEg.webp +0 -0
  155. package/renderer/dist/assets/lake-sm-CbG72Hbv.webp +0 -0
  156. package/renderer/dist/assets/lino-coast-CUZHcucI.webp +0 -0
  157. package/renderer/dist/assets/lino-coast-DP4J7cgd.webp +0 -0
  158. package/renderer/dist/assets/lino-coast-kOIAffr_.webp +0 -0
  159. package/renderer/dist/assets/lino-coast-sm-C1Mdbgit.webp +0 -0
  160. package/renderer/dist/assets/moon-pines-BskgnnXR.webp +0 -0
  161. package/renderer/dist/assets/moon-pines-CFSIIwt0.webp +0 -0
  162. package/renderer/dist/assets/moon-pines-PNhdOYww.webp +0 -0
  163. package/renderer/dist/assets/moon-pines-sm-C6K1NomK.webp +0 -0
  164. package/renderer/dist/assets/orbit-rings-2XV36Ch3.webp +0 -0
  165. package/renderer/dist/assets/orbit-rings-C4NrcYGQ.webp +0 -0
  166. package/renderer/dist/assets/orbit-rings-DGLk07dT.webp +0 -0
  167. package/renderer/dist/assets/orbit-rings-sm-Dky3YdvL.webp +0 -0
  168. package/renderer/dist/assets/peach-haze-2-C6f7xnWw.webp +0 -0
  169. package/renderer/dist/assets/peach-haze-2-sm-Dj_Ez_Sb.webp +0 -0
  170. package/renderer/dist/assets/pixel-harbour-DQjqMROg.webp +0 -0
  171. package/renderer/dist/assets/pixel-harbour-Dw71H_bc.webp +0 -0
  172. package/renderer/dist/assets/pixel-harbour-kOZfm-GM.webp +0 -0
  173. package/renderer/dist/assets/pixel-harbour-sm-Dturjuky.webp +0 -0
  174. package/renderer/dist/assets/pixel-orbit-C296JVMO.webp +0 -0
  175. package/renderer/dist/assets/pixel-orbit-C4oMRDSx.webp +0 -0
  176. package/renderer/dist/assets/pixel-orbit-CqVmQ4eh.webp +0 -0
  177. package/renderer/dist/assets/pixel-orbit-sm-CHagC_nO.webp +0 -0
  178. package/renderer/dist/assets/pixel-ridge-C6LYu6uE.webp +0 -0
  179. package/renderer/dist/assets/pixel-ridge-CIUCEESs.webp +0 -0
  180. package/renderer/dist/assets/pixel-ridge-sm-BnE0F_w-.webp +0 -0
  181. package/renderer/dist/assets/riso-hills-7heKVtry.webp +0 -0
  182. package/renderer/dist/assets/riso-hills-CIjAL3J6.webp +0 -0
  183. package/renderer/dist/assets/riso-hills-sm-DHsnbCfP.webp +0 -0
  184. package/renderer/dist/assets/slate-haze-BPWdkNsw.webp +0 -0
  185. package/renderer/dist/assets/slate-haze-sm-B0Cb5a2W.webp +0 -0
  186. package/renderer/dist/assets/valley-haze-BDHPqoJy.webp +0 -0
  187. package/renderer/dist/assets/valley-haze-sm-CTyjmPAG.webp +0 -0
  188. package/renderer/dist/assets/vector-dunes-DzzWLdKo.webp +0 -0
  189. package/renderer/dist/assets/vector-dunes-Yr9NBQUy.webp +0 -0
  190. package/renderer/dist/assets/vector-dunes-sm-D8MYY6ms.webp +0 -0
  191. package/renderer/dist/assets/watercolour-mist-BCGKN4u4.webp +0 -0
  192. package/renderer/dist/assets/watercolour-mist-BF-jk5MC.webp +0 -0
  193. package/renderer/dist/assets/watercolour-mist-sm-Ds1xzTw9.webp +0 -0
  194. package/renderer/dist/assets/woodblock-sea-B1um67Z9.webp +0 -0
  195. package/renderer/dist/assets/woodblock-sea-DKQdYs32.webp +0 -0
  196. package/renderer/dist/assets/woodblock-sea-sm-BJzNq8xh.webp +0 -0
  197. package/renderer/dist/emulator/audio-rules.mjs +0 -74
  198. package/renderer/dist/emulator/data/compression/README.md +0 -11
  199. package/renderer/dist/emulator/data/compression/extract7z.js +0 -1
  200. package/renderer/dist/emulator/data/compression/extractzip.js +0 -1
  201. package/renderer/dist/emulator/data/compression/libunrar.js +0 -519
  202. package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
  203. package/renderer/dist/emulator/data/cores/README.md +0 -22
  204. package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
  205. package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
  206. package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
  207. package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
  208. package/renderer/dist/emulator/data/cores/package.json +0 -65
  209. package/renderer/dist/emulator/data/cores/reports/mgba.json +0 -1
  210. package/renderer/dist/emulator/data/emulator.css +0 -1614
  211. package/renderer/dist/emulator/data/loader.js +0 -170
  212. package/renderer/dist/emulator/data/localization/README.md +0 -63
  213. package/renderer/dist/emulator/data/localization/af-FR.json +0 -339
  214. package/renderer/dist/emulator/data/localization/ar-AR.json +0 -301
  215. package/renderer/dist/emulator/data/localization/ben-BEN.json +0 -301
  216. package/renderer/dist/emulator/data/localization/de-GER.json +0 -301
  217. package/renderer/dist/emulator/data/localization/el-GR.json +0 -301
  218. package/renderer/dist/emulator/data/localization/en-US.json +0 -339
  219. package/renderer/dist/emulator/data/localization/es-ES.json +0 -310
  220. package/renderer/dist/emulator/data/localization/fa-AF.json +0 -310
  221. package/renderer/dist/emulator/data/localization/hi-HI.json +0 -301
  222. package/renderer/dist/emulator/data/localization/it-IT.json +0 -302
  223. package/renderer/dist/emulator/data/localization/ja-JA.json +0 -301
  224. package/renderer/dist/emulator/data/localization/jv-JV.json +0 -301
  225. package/renderer/dist/emulator/data/localization/ko-KO.json +0 -336
  226. package/renderer/dist/emulator/data/localization/pt-BR.json +0 -361
  227. package/renderer/dist/emulator/data/localization/retroarch.json +0 -617
  228. package/renderer/dist/emulator/data/localization/ro-RO.json +0 -310
  229. package/renderer/dist/emulator/data/localization/ru-RU.json +0 -301
  230. package/renderer/dist/emulator/data/localization/tr-TR.json +0 -310
  231. package/renderer/dist/emulator/data/localization/vi-VN.json +0 -302
  232. package/renderer/dist/emulator/data/localization/zh-CN.json +0 -351
  233. package/renderer/dist/emulator/data/src/GameManager.js +0 -464
  234. package/renderer/dist/emulator/data/src/compression.js +0 -142
  235. package/renderer/dist/emulator/data/src/emulator.js +0 -6158
  236. package/renderer/dist/emulator/data/src/gamepad.js +0 -167
  237. package/renderer/dist/emulator/data/src/nipplejs.js +0 -1
  238. package/renderer/dist/emulator/data/src/shaders.js +0 -236
  239. package/renderer/dist/emulator/data/src/socket.io.min.js +0 -7
  240. package/renderer/dist/emulator/data/src/storage.js +0 -125
  241. package/renderer/dist/emulator/data/version.json +0 -5
  242. package/renderer/dist/emulator/host.js +0 -299
  243. package/renderer/dist/emulator/index.html +0 -28
  244. package/renderer/dist/emulator/provenance.json +0 -6
@@ -1,16 +1,14 @@
1
1
  // A fixed allowlist exposes the instruction files, never arbitrary paths.
2
2
  // Defaults are shipped separately so an edited live file can always be reset.
3
3
  //
4
- // THERE ARE THREE OF THEM NOW, NOT FOUR (w-3dc46f3a67, 2026-09-22). Hers, on
5
- // being shown the five prompts a person could edit: "Those five layers of
6
- // prompts that the user can adjust might be overkill. Maybe we decide not to
7
- // expose some of them, or maybe we would best aggregate others."
4
+ // THERE ARE THREE OF THEM NOW, NOT FOUR (w-3dc46f3a67). Five editable layers of
5
+ // prompts was too many for a person to manage, so some were merged.
8
6
  //
9
7
  // A person opening that page is answering two questions, what should agents do
10
8
  // and how should they talk to me, so the writing rules and the finishing rules
11
- // became ONE document: `message-rules.md`, "How agents write to you". The
12
- // evidence they were one thing all along is her own rules file, where three of
13
- // four paragraphs are about how agents write to her rather than what they do.
9
+ // became ONE document: `message-rules.md`, "How agents write to you". In
10
+ // practice a rules file is mostly about how agents write to the person rather
11
+ // than what they do, which is why the two were one thing all along.
14
12
  // `joinMessageRules` below carries the two old files into the new one.
15
13
  import fs from 'node:fs';
16
14
  import path from 'node:path';
@@ -19,12 +17,11 @@ import {fileURLToPath} from 'node:url';
19
17
  // one place that holds it. Typing it here is what left three dead names in the
20
18
  // code through two renames (tests/the-app-is-named-in-one-place).
21
19
  import {nameSlug} from '../shared/product-name.mjs';
22
- // ADHD MODE IS A FOURTH FILE, NOT A PARAGRAPH INSIDE THE THIRD (w-5737fe67cf,
23
- // 2026-09-25). Hers: "it should really just append that text to your content if
24
- // you have ADHD mode on, and then remove it when you turn it off... Maybe it
25
- // opens a new section with additional instructions that get applied." Kept in
26
- // its own file so her edits to it never mix with her own rules, and survive
27
- // while the mode is off. Whether it is on lives in her config (`adhdMode`).
20
+ // ADHD MODE IS A FOURTH FILE, NOT A PARAGRAPH INSIDE THE THIRD (w-5737fe67cf).
21
+ // Its text is appended to the instructions while the mode is on and removed when
22
+ // it is off. Kept in its own file so edits to it never mix with the user's own
23
+ // rules, and survive while the mode is off. Whether it is on lives in the
24
+ // config (`adhdMode`).
28
25
  const files = {rules:'founder.md',messages:'message-rules.md',adhd:'adhd-mode.md',system:'worker.md'};
29
26
  // The two names the joined document replaced, read only by the migration.
30
27
  const WAS_MESSAGES = ['writing-rules.md','finishing.md'];
@@ -33,35 +30,38 @@ function fileFor(id) {
33
30
  if (!Object.hasOwn(files,id)) throw new Error('Unknown instruction section');
34
31
  return files[id];
35
32
  }
33
+ // WHAT THE BOX OPENS ON when the user has written nothing. The message rules
34
+ // box starts empty like the general one (w-3ec9f07978): the app's own message
35
+ // rules are not the user's to edit, because the inbox reads every message by
36
+ // them, so they ride on every run from the checkout and never sit in the box.
37
+ const boxDefault=(id)=>(id==='rules'||id==='messages') ? '' : defaults[id];
36
38
  export function readInstruction(appDir,id,shippedDir=appDir) {
37
39
  const file=fileFor(id);
38
- const defaultText=id==='rules' ? '' : defaults[id];
40
+ const defaultText=boxDefault(id);
39
41
  let text=defaultText;
40
42
  try {text=fs.readFileSync(path.join(appDir,'briefs',file),'utf8');} catch(e) {if(e.code!=='ENOENT') throw e; if(id==='system') text=readSystemTemplate(shippedDir,appDir);}
41
43
  return {text,defaultText};
42
44
  }
43
45
  /**
44
46
  * RESTORE POINTS, BECAUSE A SAVE USED TO BE THE END OF THE OLD TEXT
45
- * (w-94b3af4e70, 2026-09-28).
47
+ * (w-94b3af4e70).
46
48
  *
47
- * Hers, on why the page needs this at all: "a user might change the agent
48
- * instructions and lose some critical information to the point where maybe the
49
- * app doesn't work the way they want it or they think, 'It worked better
50
- * before. I want to restore it.'"
49
+ * Why the page needs this at all: a user can edit the agent instructions, lose
50
+ * something that mattered, and want an earlier version back.
51
51
  *
52
- * Until this, the only thing the page could hand back was our shipped text, and
53
- * on her Mac that means deleting about 21,000 characters of her own writing to
54
- * recover 5,654 of ours. The box saves 400ms after a keystroke, straight over
52
+ * Until this, the only thing the page could hand back was our shipped text, so
53
+ * recovering meant throwing away everything the user had written to get the
54
+ * default. The box saves 400ms after a keystroke, straight over
55
55
  * the file, so a paragraph deleted at noon was gone at noon.
56
56
  *
57
57
  * WHAT COUNTS AS A VERSION: the text that was in the file before a save, kept
58
58
  * under the time that text was itself saved, so the list reads as the versions
59
- * she wrote rather than as a log of keystrokes. A burst of typing is one
59
+ * the user wrote rather than as a log of keystrokes. A burst of typing is one
60
60
  * version and not two hundred, because text that has been sitting untouched for
61
61
  * less than QUIET_MS is still being written and is not a place to come back to.
62
62
  *
63
63
  * It never throws into a save. Losing a restore point is a small thing and
64
- * failing to write what she just typed is not.
64
+ * failing to write what the user just typed is not.
65
65
  */
66
66
  const HISTORY='instruction-history';
67
67
  const KEEP=20;
@@ -84,13 +84,13 @@ export function readVersion(appDir,id,ts) {
84
84
  if(!Number.isFinite(at)) throw new Error('Unknown version');
85
85
  return {text:fs.readFileSync(path.join(historyDir(appDir,id),`${at}.md`),'utf8')};
86
86
  }
87
- function keepVersion(appDir,id,now=Date.now()) {
87
+ function keepVersion(appDir,id,now=Date.now(),always=false) {
88
88
  try {
89
89
  const live=path.join(appDir,'briefs',fileFor(id));
90
90
  let stat,text;
91
91
  try {stat=fs.statSync(live); text=fs.readFileSync(live,'utf8');}
92
92
  catch(e) {if(e.code!=='ENOENT') throw e; return;}
93
- if(now-stat.mtimeMs<QUIET_MS) return;
93
+ if(!always&&now-stat.mtimeMs<QUIET_MS) return;
94
94
  const ts=Math.floor(stat.mtimeMs);
95
95
  const have=listVersions(appDir,id);
96
96
  if(have.some(v=>v.ts===ts)) return;
@@ -123,21 +123,20 @@ export function readSystemTemplate(appDir,dataDir=appDir) {
123
123
  }
124
124
 
125
125
  /**
126
- * HER OWN WRITING, CARRIED OUT OF THE CHECKOUT ONCE (w-3dc46f3a67, 2026-09-21).
126
+ * THE USER'S OWN WRITING, CARRIED OUT OF THE CHECKOUT ONCE (w-3dc46f3a67).
127
127
  *
128
128
  * Until this ran, the four boxes in Settings read and wrote `dataDir/briefs`,
129
129
  * and running from source `dataDir` is the git checkout the whole fleet works
130
- * in. Measured on her repo that day: briefs/founder.md had four commits, all of
131
- * them an agent's, and the working copy matched the newest exactly. Her rules
132
- * were being kept in the one folder every worker is allowed to `git checkout`.
130
+ * in. So the user's rules were being kept in the one folder every worker is
131
+ * allowed to `git checkout`, and agents had been committing to them.
133
132
  *
134
- * WHAT MOVES IS ONLY WHAT SHE WROTE, and the test for that is the shipped
133
+ * WHAT MOVES IS ONLY WHAT THE USER WROTE, and the test for that is the shipped
135
134
  * default. A file whose text IS the default is our file sitting where we put
136
- * it, and copying it into her folder would freeze it there: the next release's
137
- * worker brief would never reach her, because her "edit" would shadow it
138
- * forever. So a file is carried across only when it differs from the default,
139
- * which on her Mac is founder.md and writing-rules.md and neither of the two we
140
- * ship. Those two are the same pair package.json keeps out of the download.
135
+ * it, and copying it into the user's folder would freeze it there: the next
136
+ * release's worker brief would never reach them, because the "edit" would
137
+ * shadow it forever. So a file is carried across only when it differs from the
138
+ * default, which is typically founder.md and writing-rules.md and neither of the
139
+ * two we ship. Those two are the same pair package.json keeps out of the download.
141
140
  *
142
141
  * It is a COPY, not a move, and it never overwrites. The checkout keeps its
143
142
  * copy so that merging this cannot lose a word, and a second run finds the
@@ -165,24 +164,33 @@ export function carryHerBriefsAcross(fromDir,toDir) {
165
164
  }
166
165
 
167
166
  /**
168
- * THE TWO OLD MESSAGE FILES, JOINED INTO ONE, ONCE (w-3dc46f3a67, 2026-09-22).
167
+ * THE TWO OLD MESSAGE FILES, JOINED INTO ONE, ONCE (w-3dc46f3a67).
169
168
  *
170
- * `writing-rules.md` said how an agent writes to her during a task and
169
+ * `writing-rules.md` said how an agent writes to the user during a task and
171
170
  * `finishing.md` said how it writes the last message. They are one box now, so
172
171
  * they have to become one document, and this is where that happens: on the
173
172
  * first boot after the change, before anything reads the new name.
174
173
  *
175
- * WHAT IT DOES WITH THE HALF SHE NEVER WROTE. The writing rules ship empty, so
176
- * anything in that file is hers. The finishing rules ship with 5,794 characters
177
- * of text, so most people have never touched them. When she has the one and not
178
- * the other, the joined document takes the shipped finishing text as its second
179
- * half rather than leaving it out: her copy shadows the default from here on,
180
- * and dropping it would quietly stop every task ending the way it does today.
174
+ * WHAT IT DOES WITH THE HALF THE USER NEVER WROTE. The writing rules ship
175
+ * empty, so anything in that file is the user's. The finishing rules ship with
176
+ * 5,794 characters of text, so most people have never touched them. When the
177
+ * user has the one and not the other, the joined document takes the shipped
178
+ * finishing text as its second half rather than leaving it out: the user's copy
179
+ * shadows the default from here on, and dropping it would quietly stop every
180
+ * task ending the way it does today.
181
181
  *
182
- * AND WHEN SHE WROTE NEITHER, IT WRITES NOTHING AT ALL. That is the fresh
183
- * install, and leaving her with no file of her own is what keeps a later
184
- * release's default reaching her. Same rule as `carryHerBriefsAcross` above:
185
- * never freeze our own text into her folder.
182
+ * AND WHEN THE USER WROTE NEITHER, IT WRITES NOTHING AT ALL. That is the fresh
183
+ * install, and leaving them with no file of their own is what keeps a later
184
+ * release's default reaching them. Same rule as `carryHerBriefsAcross` above:
185
+ * never freeze our own text into the user's folder.
186
+ *
187
+ * IT READS ONLY THE USER'S FOLDER, AND ADDS NOTHING OF OURS (w-3ec9f07978).
188
+ * It used to fall back to the checkout's copies and to put the shipped ending
189
+ * under whatever it found. In this repository `writing-rules.md` ships 16,000
190
+ * characters long, so every fresh copy run from source joined our two files
191
+ * and saved them as the user's, and the box showed nothing but our machinery.
192
+ * The shipped ending rides on every run anyway now (`messageRules` in the
193
+ * supervisor), so there is nothing of ours left to carry.
186
194
  *
187
195
  * Never throws, never overwrites. Returns what it joined, or null.
188
196
  */
@@ -190,16 +198,46 @@ export function joinMessageRules(shippedDir,toDir) {
190
198
  try {
191
199
  const target=path.join(toDir,'briefs',files.messages);
192
200
  if(fs.existsSync(target)) return null;
193
- const read=(dir,name)=>{try {return fs.readFileSync(path.join(dir,'briefs',name),'utf8');} catch {return null;}};
194
- const either=(name)=>read(toDir,name) ?? read(shippedDir,name);
195
- const [writing,finishing]=WAS_MESSAGES.map((name)=>(either(name) ?? '').trim());
201
+ const read=(name)=>{try {return fs.readFileSync(path.join(toDir,'briefs',name),'utf8');} catch {return null;}};
202
+ const [writing,finishing]=WAS_MESSAGES.map((name)=>(read(name) ?? '').trim());
196
203
  if(!writing && !finishing) return null;
197
- const ending=finishing || defaults.messages.trim();
198
- const joined=[writing,ending].filter(Boolean).join('\n\n');
204
+ const joined=[writing,finishing].filter(Boolean).join('\n\n');
199
205
  writeInstruction(toDir,'messages',`${joined}\n`);
200
- return {writing:writing.length,ending:ending.length,total:joined.length+1};
206
+ return {writing:writing.length,ending:finishing.length,total:joined.length+1};
201
207
  } catch(e) {
202
208
  console.warn(`${nameSlug}: could not join the message rules: ${e.message}`);
203
209
  return null;
204
210
  }
205
211
  }
212
+
213
+ /**
214
+ * A MESSAGE BOX THAT HOLDS ONLY OUR TEXT IS NOT THE USER'S, ONCE (w-3ec9f07978).
215
+ *
216
+ * The old join above wrote the checkout's writing rules and ending rules into
217
+ * the user's folder as if they had typed them, so their box opened on 21,743
218
+ * characters of the app's own rules. A file whose text is EXACTLY one of the
219
+ * things we ship, alone or joined the way the old join joined them, is set
220
+ * aside: kept as a restore point and removed, so the box opens empty and our
221
+ * rules reach the run from the checkout as they should.
222
+ *
223
+ * Exact only. One line of the user's own makes the whole file theirs, and
224
+ * nothing here tries to cut it apart. Never throws. Returns whether it acted.
225
+ */
226
+ export function setAsideShippedMessageRules(shippedDir,toDir) {
227
+ try {
228
+ const target=path.join(toDir,'briefs',files.messages);
229
+ if(path.resolve(shippedDir)===path.resolve(toDir)||!fs.existsSync(target)) return false;
230
+ const read=(name)=>{try {return fs.readFileSync(path.join(shippedDir,'briefs',name),'utf8').trim();} catch {return '';}};
231
+ const writing=read(WAS_MESSAGES[0]);
232
+ const endings=[...new Set([read(files.messages),defaults.messages.trim()])].filter(Boolean);
233
+ const ours=new Set([...endings,...endings.map((e)=>[writing,e].filter(Boolean).join('\n\n'))]);
234
+ const text=fs.readFileSync(target,'utf8').trim();
235
+ if(!text||!ours.has(text)) return false;
236
+ keepVersion(toDir,'messages',Date.now(),true);
237
+ fs.rmSync(target,{force:true});
238
+ return true;
239
+ } catch(e) {
240
+ console.warn(`${nameSlug}: could not set aside the shipped message rules: ${e.message}`);
241
+ return false;
242
+ }
243
+ }