aicodeman 1.45.0 → 1.46.0

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 (283) hide show
  1. package/README.md +26 -6
  2. package/dist/agent-codex-identity.d.ts +26 -0
  3. package/dist/agent-codex-identity.d.ts.map +1 -0
  4. package/dist/agent-codex-identity.js +253 -0
  5. package/dist/agent-codex-identity.js.map +1 -0
  6. package/dist/agent-inspection.d.ts +32 -0
  7. package/dist/agent-inspection.d.ts.map +1 -0
  8. package/dist/agent-inspection.js +196 -0
  9. package/dist/agent-inspection.js.map +1 -0
  10. package/dist/agent-staging.d.ts +130 -0
  11. package/dist/agent-staging.d.ts.map +1 -0
  12. package/dist/agent-staging.js +312 -0
  13. package/dist/agent-staging.js.map +1 -0
  14. package/dist/cli-agent.d.ts +31 -2
  15. package/dist/cli-agent.d.ts.map +1 -1
  16. package/dist/cli-agent.js +279 -34
  17. package/dist/cli-agent.js.map +1 -1
  18. package/dist/cli-history.d.ts +20 -0
  19. package/dist/cli-history.d.ts.map +1 -0
  20. package/dist/cli-history.js +61 -0
  21. package/dist/cli-history.js.map +1 -0
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +6 -1
  24. package/dist/cli.js.map +1 -1
  25. package/dist/codex-rollout-format.d.ts +66 -0
  26. package/dist/codex-rollout-format.d.ts.map +1 -0
  27. package/dist/codex-rollout-format.js +111 -0
  28. package/dist/codex-rollout-format.js.map +1 -0
  29. package/dist/codex-transcript.d.ts +3 -0
  30. package/dist/codex-transcript.d.ts.map +1 -1
  31. package/dist/codex-transcript.js +13 -41
  32. package/dist/codex-transcript.js.map +1 -1
  33. package/dist/config/cli-registry/schema.d.ts +12 -7
  34. package/dist/config/cli-registry/schema.d.ts.map +1 -1
  35. package/dist/config/cli-registry/schema.js +26 -0
  36. package/dist/config/cli-registry/schema.js.map +1 -1
  37. package/dist/config/cli-registry/stock.d.ts.map +1 -1
  38. package/dist/config/cli-registry/stock.js +40 -1
  39. package/dist/config/cli-registry/stock.js.map +1 -1
  40. package/dist/config/cli-registry/types.d.ts +57 -0
  41. package/dist/config/cli-registry/types.d.ts.map +1 -1
  42. package/dist/config/server-timing.d.ts +6 -0
  43. package/dist/config/server-timing.d.ts.map +1 -1
  44. package/dist/config/server-timing.js +9 -0
  45. package/dist/config/server-timing.js.map +1 -1
  46. package/dist/daemon-control.d.ts +1 -0
  47. package/dist/daemon-control.d.ts.map +1 -1
  48. package/dist/daemon-control.js +2 -0
  49. package/dist/daemon-control.js.map +1 -1
  50. package/dist/deepseek-transcript.d.ts +9 -7
  51. package/dist/deepseek-transcript.d.ts.map +1 -1
  52. package/dist/deepseek-transcript.js +9 -7
  53. package/dist/deepseek-transcript.js.map +1 -1
  54. package/dist/docker-hosts.d.ts +8 -0
  55. package/dist/docker-hosts.d.ts.map +1 -1
  56. package/dist/docker-hosts.js +29 -0
  57. package/dist/docker-hosts.js.map +1 -1
  58. package/dist/git-fetch-scheduler.d.ts +124 -0
  59. package/dist/git-fetch-scheduler.d.ts.map +1 -0
  60. package/dist/git-fetch-scheduler.js +249 -0
  61. package/dist/git-fetch-scheduler.js.map +1 -0
  62. package/dist/git-fetch.d.ts +241 -0
  63. package/dist/git-fetch.d.ts.map +1 -0
  64. package/dist/git-fetch.js +392 -0
  65. package/dist/git-fetch.js.map +1 -0
  66. package/dist/git-workspace-status.d.ts +36 -1
  67. package/dist/git-workspace-status.d.ts.map +1 -1
  68. package/dist/git-workspace-status.js +42 -5
  69. package/dist/git-workspace-status.js.map +1 -1
  70. package/dist/hooks-config.d.ts +14 -2
  71. package/dist/hooks-config.d.ts.map +1 -1
  72. package/dist/hooks-config.js +39 -3
  73. package/dist/hooks-config.js.map +1 -1
  74. package/dist/local-history.d.ts +71 -0
  75. package/dist/local-history.d.ts.map +1 -0
  76. package/dist/local-history.js +750 -0
  77. package/dist/local-history.js.map +1 -0
  78. package/dist/mcp-server.d.ts.map +1 -1
  79. package/dist/mcp-server.js +2 -2
  80. package/dist/mcp-server.js.map +1 -1
  81. package/dist/mux-interface.d.ts +5 -2
  82. package/dist/mux-interface.d.ts.map +1 -1
  83. package/dist/prompt-cache-keepwarm.d.ts.map +1 -1
  84. package/dist/prompt-cache-keepwarm.js +5 -3
  85. package/dist/prompt-cache-keepwarm.js.map +1 -1
  86. package/dist/session.d.ts +113 -0
  87. package/dist/session.d.ts.map +1 -1
  88. package/dist/session.js +341 -13
  89. package/dist/session.js.map +1 -1
  90. package/dist/terminal-input-classifier.d.ts +8 -0
  91. package/dist/terminal-input-classifier.d.ts.map +1 -0
  92. package/dist/terminal-input-classifier.js +60 -0
  93. package/dist/terminal-input-classifier.js.map +1 -0
  94. package/dist/tmux-manager.d.ts +23 -1
  95. package/dist/tmux-manager.d.ts.map +1 -1
  96. package/dist/tmux-manager.js +54 -6
  97. package/dist/tmux-manager.js.map +1 -1
  98. package/dist/types/session.d.ts +15 -0
  99. package/dist/types/session.d.ts.map +1 -1
  100. package/dist/types/session.js.map +1 -1
  101. package/dist/web/network-auth-policy.d.ts +13 -3
  102. package/dist/web/network-auth-policy.d.ts.map +1 -1
  103. package/dist/web/network-auth-policy.js +15 -3
  104. package/dist/web/network-auth-policy.js.map +1 -1
  105. package/dist/web/ports/infra-port.d.ts +2 -0
  106. package/dist/web/ports/infra-port.d.ts.map +1 -1
  107. package/dist/web/public/admin-ui.js.gz +0 -0
  108. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  109. package/dist/web/public/app.3093527f.js +43 -0
  110. package/dist/web/public/app.3093527f.js.br +0 -0
  111. package/dist/web/public/app.3093527f.js.gz +0 -0
  112. package/dist/web/public/approvals-ui.js.gz +0 -0
  113. package/dist/web/public/{constants.0b3cdde9.js → constants.affcdde6.js} +611 -12
  114. package/dist/web/public/constants.affcdde6.js.br +0 -0
  115. package/dist/web/public/constants.affcdde6.js.gz +0 -0
  116. package/dist/web/public/cron-ui.js.gz +0 -0
  117. package/dist/web/public/entrance-animations.js.gz +0 -0
  118. package/dist/web/public/git-status-ui.js +88 -5
  119. package/dist/web/public/git-status-ui.js.br +0 -0
  120. package/dist/web/public/git-status-ui.js.gz +0 -0
  121. package/dist/web/public/header-divider.eb2d2463.js.gz +0 -0
  122. package/dist/web/public/home-sessions.js.gz +0 -0
  123. package/dist/web/public/host-wake-ui.js.gz +0 -0
  124. package/dist/web/public/i18n.70265257.js +2 -0
  125. package/dist/web/public/i18n.70265257.js.br +0 -0
  126. package/dist/web/public/i18n.70265257.js.gz +0 -0
  127. package/dist/web/public/image-input.941386e7.js.gz +0 -0
  128. package/dist/web/public/index.html +107 -16
  129. package/dist/web/public/index.html.br +0 -0
  130. package/dist/web/public/index.html.gz +0 -0
  131. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  132. package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
  133. package/dist/web/public/{mobile-handlers.1c0cbcd8.js → mobile-handlers.f7c3f692.js} +22 -0
  134. package/dist/web/public/mobile-handlers.f7c3f692.js.br +0 -0
  135. package/dist/web/public/mobile-handlers.f7c3f692.js.gz +0 -0
  136. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  137. package/dist/web/public/mobile-overview.js.gz +0 -0
  138. package/dist/web/public/mobile.1bbd2d67.css +1 -0
  139. package/dist/web/public/mobile.1bbd2d67.css.br +0 -0
  140. package/dist/web/public/mobile.1bbd2d67.css.gz +0 -0
  141. package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
  142. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  143. package/dist/web/public/{panels-ui.78c3defb.js → panels-ui.4a56ee63.js} +1 -1
  144. package/dist/web/public/panels-ui.4a56ee63.js.br +0 -0
  145. package/dist/web/public/panels-ui.4a56ee63.js.gz +0 -0
  146. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  147. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  148. package/dist/web/public/readmymind-ui.js.gz +0 -0
  149. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  150. package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
  151. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  152. package/dist/web/public/session-lineage.js.gz +0 -0
  153. package/dist/web/public/{session-ui.ea6b1994.js → session-ui.186f9419.js} +8 -8
  154. package/dist/web/public/session-ui.186f9419.js.br +0 -0
  155. package/dist/web/public/session-ui.186f9419.js.gz +0 -0
  156. package/dist/web/public/{settings-ui.085dd202.js → settings-ui.a191154e.js} +11 -11
  157. package/dist/web/public/settings-ui.a191154e.js.br +0 -0
  158. package/dist/web/public/settings-ui.a191154e.js.gz +0 -0
  159. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  160. package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
  161. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  162. package/dist/web/public/styles.7238eae8.css +1 -0
  163. package/dist/web/public/styles.7238eae8.css.br +0 -0
  164. package/dist/web/public/styles.7238eae8.css.gz +0 -0
  165. package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
  166. package/dist/web/public/sw.js +2 -2
  167. package/dist/web/public/sw.js.br +0 -0
  168. package/dist/web/public/sw.js.gz +0 -0
  169. package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
  170. package/dist/web/public/{tab-rail-resize.7157e0bd.js → tab-rail-resize.b5cdceaf.js} +1 -1
  171. package/dist/web/public/tab-rail-resize.b5cdceaf.js.br +0 -0
  172. package/dist/web/public/tab-rail-resize.b5cdceaf.js.gz +0 -0
  173. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  174. package/dist/web/public/terminal-split.js.gz +0 -0
  175. package/dist/web/public/terminal-tile.js +2 -0
  176. package/dist/web/public/terminal-tile.js.br +0 -0
  177. package/dist/web/public/terminal-tile.js.gz +0 -0
  178. package/dist/web/public/terminal-ui.7e787829.js +4 -0
  179. package/dist/web/public/terminal-ui.7e787829.js.br +0 -0
  180. package/dist/web/public/terminal-ui.7e787829.js.gz +0 -0
  181. package/dist/web/public/tile-grid.js.gz +0 -0
  182. package/dist/web/public/ultracode-panel.js.gz +0 -0
  183. package/dist/web/public/ultracode-windows.js.gz +0 -0
  184. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  185. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  186. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  187. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  188. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  189. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  190. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  191. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  192. package/dist/web/public/vendor/{xterm-predictive-echo.ae5e3d5d.js → xterm-predictive-echo.276b11d9.js} +1 -1
  193. package/dist/web/public/vendor/xterm-predictive-echo.276b11d9.js.br +0 -0
  194. package/dist/web/public/vendor/xterm-predictive-echo.276b11d9.js.gz +0 -0
  195. package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js +5 -0
  196. package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js.br +0 -0
  197. package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js.gz +0 -0
  198. package/dist/web/public/vendor/xterm.css.gz +0 -0
  199. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  200. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  201. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  202. package/dist/web/public/webview-tabs.js.gz +0 -0
  203. package/dist/web/public/whats-new.json +9 -9
  204. package/dist/web/routes/agent-session-routes.d.ts +20 -0
  205. package/dist/web/routes/agent-session-routes.d.ts.map +1 -0
  206. package/dist/web/routes/agent-session-routes.js +160 -0
  207. package/dist/web/routes/agent-session-routes.js.map +1 -0
  208. package/dist/web/routes/case-routes.d.ts.map +1 -1
  209. package/dist/web/routes/case-routes.js +32 -26
  210. package/dist/web/routes/case-routes.js.map +1 -1
  211. package/dist/web/routes/config-backup-routes.js +2 -2
  212. package/dist/web/routes/config-backup-routes.js.map +1 -1
  213. package/dist/web/routes/git-status-routes.d.ts +21 -7
  214. package/dist/web/routes/git-status-routes.d.ts.map +1 -1
  215. package/dist/web/routes/git-status-routes.js +78 -12
  216. package/dist/web/routes/git-status-routes.js.map +1 -1
  217. package/dist/web/routes/index.d.ts +1 -0
  218. package/dist/web/routes/index.d.ts.map +1 -1
  219. package/dist/web/routes/index.js +1 -0
  220. package/dist/web/routes/index.js.map +1 -1
  221. package/dist/web/routes/ralph-routes.d.ts.map +1 -1
  222. package/dist/web/routes/ralph-routes.js +7 -16
  223. package/dist/web/routes/ralph-routes.js.map +1 -1
  224. package/dist/web/routes/session-routes.d.ts.map +1 -1
  225. package/dist/web/routes/session-routes.js +96 -28
  226. package/dist/web/routes/session-routes.js.map +1 -1
  227. package/dist/web/routes/system-routes.d.ts.map +1 -1
  228. package/dist/web/routes/system-routes.js +84 -57
  229. package/dist/web/routes/system-routes.js.map +1 -1
  230. package/dist/web/routes/ws-routes.d.ts +5 -0
  231. package/dist/web/routes/ws-routes.d.ts.map +1 -1
  232. package/dist/web/routes/ws-routes.js +84 -47
  233. package/dist/web/routes/ws-routes.js.map +1 -1
  234. package/dist/web/schemas.d.ts +30 -7
  235. package/dist/web/schemas.d.ts.map +1 -1
  236. package/dist/web/schemas.js +40 -1
  237. package/dist/web/schemas.js.map +1 -1
  238. package/dist/web/server.d.ts +5 -2
  239. package/dist/web/server.d.ts.map +1 -1
  240. package/dist/web/server.js +84 -14
  241. package/dist/web/server.js.map +1 -1
  242. package/dist/web/settings-writer.d.ts +47 -0
  243. package/dist/web/settings-writer.d.ts.map +1 -0
  244. package/dist/web/settings-writer.js +110 -0
  245. package/dist/web/settings-writer.js.map +1 -0
  246. package/package.json +1 -1
  247. package/skills/codeman/SKILL.md +25 -6
  248. package/skills/codeman/reference/history.md +84 -0
  249. package/skills/codeman/reference/inspection-and-staging.md +152 -0
  250. package/skills/codeman/reference/messaging.md +1 -1
  251. package/skills/codeman/reference/verbs.md +18 -3
  252. package/dist/web/public/app.42d0abae.js +0 -43
  253. package/dist/web/public/app.42d0abae.js.br +0 -0
  254. package/dist/web/public/app.42d0abae.js.gz +0 -0
  255. package/dist/web/public/constants.0b3cdde9.js.br +0 -0
  256. package/dist/web/public/constants.0b3cdde9.js.gz +0 -0
  257. package/dist/web/public/i18n.1e68220d.js +0 -2
  258. package/dist/web/public/i18n.1e68220d.js.br +0 -0
  259. package/dist/web/public/i18n.1e68220d.js.gz +0 -0
  260. package/dist/web/public/mobile-handlers.1c0cbcd8.js.br +0 -0
  261. package/dist/web/public/mobile-handlers.1c0cbcd8.js.gz +0 -0
  262. package/dist/web/public/mobile.12fb18de.css +0 -1
  263. package/dist/web/public/mobile.12fb18de.css.br +0 -0
  264. package/dist/web/public/mobile.12fb18de.css.gz +0 -0
  265. package/dist/web/public/panels-ui.78c3defb.js.br +0 -0
  266. package/dist/web/public/panels-ui.78c3defb.js.gz +0 -0
  267. package/dist/web/public/session-ui.ea6b1994.js.br +0 -0
  268. package/dist/web/public/session-ui.ea6b1994.js.gz +0 -0
  269. package/dist/web/public/settings-ui.085dd202.js.br +0 -0
  270. package/dist/web/public/settings-ui.085dd202.js.gz +0 -0
  271. package/dist/web/public/styles.3b59b41d.css +0 -1
  272. package/dist/web/public/styles.3b59b41d.css.br +0 -0
  273. package/dist/web/public/styles.3b59b41d.css.gz +0 -0
  274. package/dist/web/public/tab-rail-resize.7157e0bd.js.br +0 -0
  275. package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
  276. package/dist/web/public/terminal-ui.fb2a27f5.js +0 -2
  277. package/dist/web/public/terminal-ui.fb2a27f5.js.br +0 -0
  278. package/dist/web/public/terminal-ui.fb2a27f5.js.gz +0 -0
  279. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.br +0 -0
  280. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  281. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js +0 -4
  282. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.br +0 -0
  283. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @fileoverview The writer of the server's `settings.json` (`SETTINGS_PATH`).
3
+ *
4
+ * Every read-modify-write of that file goes through `mutateSettingsFile()`, the same shape as
5
+ * `mutateRegistryFile()` (config/cli-registry/registry-writer.ts). The one direct writer is
6
+ * config-backup restore (`restoreConfigBackup()`, as for `clis.json`): it must work on a file that
7
+ * does not parse, which this writer refuses.
8
+ *
9
+ * - **Serialized.** Mutations run one at a time on a single promise chain, each one reading,
10
+ * changing and writing before the next starts. The writers used to run unserialized with a
11
+ * plain truncate-then-write `fs.writeFile`, and quick-start did not even await its
12
+ * `lastUsedCase` write, so a quick-start followed by a settings save could tear the file (one
13
+ * write's bytes over the tail of a longer one) or drop the other writer's key.
14
+ * - **Atomic.** Each write goes to its own temp file beside the target and is renamed over it,
15
+ * so a reader never sees a half-written file.
16
+ * - **Refuses a file it must not trust.** Only a missing (or empty) file starts from `{}`. A file
17
+ * that cannot be read or does not parse is left alone and the write throws
18
+ * `SettingsWriteRefusedError`: the writers used to read such a file as `{}` and write back only
19
+ * their own keys, which erased every other setting.
20
+ */
21
+ import { type ApiResponse } from '../types.js';
22
+ export type SettingsFile = Record<string, unknown>;
23
+ /** A write refused because the existing `settings.json` must not be overwritten. The message is user-facing. */
24
+ export declare class SettingsWriteRefusedError extends Error {
25
+ constructor(message: string);
26
+ }
27
+ /**
28
+ * Run one change to `settings.json`. `fn` receives the current settings and returns the settings
29
+ * to write, or `undefined` to write nothing. Resolves with the settings before and after the
30
+ * change (`after` is `undefined` when nothing was written). A refused or failed mutation rejects
31
+ * its own caller only; the chain keeps going.
32
+ */
33
+ export declare function mutateSettingsFile(fn: (current: SettingsFile) => SettingsFile | undefined): Promise<{
34
+ before: SettingsFile;
35
+ after: SettingsFile | undefined;
36
+ }>;
37
+ /**
38
+ * Rejects with `SettingsWriteRefusedError` when `mutateSettingsFile()` would refuse a write right
39
+ * now: the writer's own read-for-write rule, run on its own. For a route that does something it
40
+ * cannot take back (case rename ends sessions) before its settings write. A check only, outside
41
+ * the chain (writes are atomic, so it never reads a half-written file); the file can still change
42
+ * before the write.
43
+ */
44
+ export declare function assertSettingsWritable(): Promise<void>;
45
+ /** A refused write answers 409 CONFLICT with the fix; any other failure stays OPERATION_FAILED. */
46
+ export declare function settingsWriteErrorResponse(err: unknown): ApiResponse<never>;
47
+ //# sourceMappingURL=settings-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-writer.d.ts","sourceRoot":"","sources":["../../src/web/settings-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,OAAO,EAAsD,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAGnG,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnD,gHAAgH;AAChH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,OAAO,EAAE,MAAM;CAI5B;AA6CD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,YAAY,GAAG,SAAS,GACtD,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAA;CAAE,CAAC,CAYpE;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAE5D;AAED,mGAAmG;AACnG,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAG3E"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @fileoverview The writer of the server's `settings.json` (`SETTINGS_PATH`).
3
+ *
4
+ * Every read-modify-write of that file goes through `mutateSettingsFile()`, the same shape as
5
+ * `mutateRegistryFile()` (config/cli-registry/registry-writer.ts). The one direct writer is
6
+ * config-backup restore (`restoreConfigBackup()`, as for `clis.json`): it must work on a file that
7
+ * does not parse, which this writer refuses.
8
+ *
9
+ * - **Serialized.** Mutations run one at a time on a single promise chain, each one reading,
10
+ * changing and writing before the next starts. The writers used to run unserialized with a
11
+ * plain truncate-then-write `fs.writeFile`, and quick-start did not even await its
12
+ * `lastUsedCase` write, so a quick-start followed by a settings save could tear the file (one
13
+ * write's bytes over the tail of a longer one) or drop the other writer's key.
14
+ * - **Atomic.** Each write goes to its own temp file beside the target and is renamed over it,
15
+ * so a reader never sees a half-written file.
16
+ * - **Refuses a file it must not trust.** Only a missing (or empty) file starts from `{}`. A file
17
+ * that cannot be read or does not parse is left alone and the write throws
18
+ * `SettingsWriteRefusedError`: the writers used to read such a file as `{}` and write back only
19
+ * their own keys, which erased every other setting.
20
+ */
21
+ import { randomUUID } from 'node:crypto';
22
+ import fs from 'node:fs/promises';
23
+ import { dirname } from 'node:path';
24
+ import { ApiErrorCode, createErrorResponse, getErrorMessage } from '../types.js';
25
+ import { SETTINGS_PATH } from './route-helpers.js';
26
+ /** A write refused because the existing `settings.json` must not be overwritten. The message is user-facing. */
27
+ export class SettingsWriteRefusedError extends Error {
28
+ constructor(message) {
29
+ super(message);
30
+ this.name = 'SettingsWriteRefusedError';
31
+ }
32
+ }
33
+ async function readSettingsForWrite(path) {
34
+ let raw;
35
+ try {
36
+ raw = await fs.readFile(path, 'utf-8');
37
+ }
38
+ catch (err) {
39
+ if (err.code === 'ENOENT')
40
+ return {};
41
+ throw new SettingsWriteRefusedError(`Cannot read ${path} (${err.message}), so this change was not saved.`);
42
+ }
43
+ // An empty file holds no settings to lose.
44
+ if (raw.trim() === '')
45
+ return {};
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(raw);
49
+ }
50
+ catch {
51
+ parsed = undefined;
52
+ }
53
+ // The parser's own message is not repeated: it can quote the file, which may hold an API key.
54
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
55
+ throw new SettingsWriteRefusedError(`${path} is not a valid JSON object, so this change was not saved (saving would have replaced every other setting in it). Fix or delete the file, or restore it from a config backup, then try again.`);
56
+ }
57
+ return parsed;
58
+ }
59
+ async function writeSettingsFile(path, settings) {
60
+ // Through a symlink, as the plain writeFile this replaces did: the rename lands on the real file.
61
+ const target = await fs.realpath(path).catch(() => path);
62
+ await fs.mkdir(dirname(target), { recursive: true });
63
+ const tmp = `${target}.${process.pid}.${randomUUID()}.tmp`;
64
+ try {
65
+ await fs.writeFile(tmp, JSON.stringify(settings, null, 2), { mode: 0o600 });
66
+ await fs.rename(tmp, target);
67
+ }
68
+ catch (err) {
69
+ await fs.rm(tmp, { force: true }).catch(() => { });
70
+ throw err;
71
+ }
72
+ }
73
+ let mutationChain = Promise.resolve();
74
+ /**
75
+ * Run one change to `settings.json`. `fn` receives the current settings and returns the settings
76
+ * to write, or `undefined` to write nothing. Resolves with the settings before and after the
77
+ * change (`after` is `undefined` when nothing was written). A refused or failed mutation rejects
78
+ * its own caller only; the chain keeps going.
79
+ */
80
+ export function mutateSettingsFile(fn) {
81
+ const run = mutationChain.then(async () => {
82
+ const before = await readSettingsForWrite(SETTINGS_PATH).catch((err) => {
83
+ console.warn(`[settings] write refused: ${getErrorMessage(err)}`);
84
+ throw err;
85
+ });
86
+ const after = fn({ ...before });
87
+ if (after)
88
+ await writeSettingsFile(SETTINGS_PATH, after);
89
+ return { before, after };
90
+ });
91
+ mutationChain = run.catch(() => { });
92
+ return run;
93
+ }
94
+ /**
95
+ * Rejects with `SettingsWriteRefusedError` when `mutateSettingsFile()` would refuse a write right
96
+ * now: the writer's own read-for-write rule, run on its own. For a route that does something it
97
+ * cannot take back (case rename ends sessions) before its settings write. A check only, outside
98
+ * the chain (writes are atomic, so it never reads a half-written file); the file can still change
99
+ * before the write.
100
+ */
101
+ export async function assertSettingsWritable() {
102
+ await readSettingsForWrite(SETTINGS_PATH);
103
+ }
104
+ /** A refused write answers 409 CONFLICT with the fix; any other failure stays OPERATION_FAILED. */
105
+ export function settingsWriteErrorResponse(err) {
106
+ if (err instanceof SettingsWriteRefusedError)
107
+ return createErrorResponse(ApiErrorCode.CONFLICT, err.message);
108
+ return createErrorResponse(ApiErrorCode.OPERATION_FAILED, getErrorMessage(err));
109
+ }
110
+ //# sourceMappingURL=settings-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-writer.js","sourceRoot":"","sources":["../../src/web/settings-writer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicodeman",
3
- "version": "1.45.0",
3
+ "version": "1.46.0",
4
4
  "description": "Self-hosted mission control for AI coding agents: run Claude Code, Codex, OpenCode, Gemini, DeepSeek, Grok and more 24/7 in tmux, from any device.",
5
5
  "mcpName": "io.github.Ark0N/codeman",
6
6
  "type": "module",
@@ -8,12 +8,19 @@ description: >-
8
8
  read their output, and clean up; where available, message claude workers directly
9
9
  (Claude Code cross-session messaging). Use when asked to orchestrate or parallelize
10
10
  work across Codeman sessions, get a second opinion from another CLI or model, watch
11
- another session, or start and manage workers. Only usable inside a Codeman-managed
12
- session (CODEMAN_MUX=1); refuse to act otherwise.
11
+ another session, start and manage workers, or retrieve saved local Codex history
12
+ by file/native ID or root and descendants with topic/date filters. Live API
13
+ operations require a Codeman-managed session (CODEMAN_MUX=1). Offline history
14
+ reads do not require a server or managed session.
13
15
  ---
14
16
 
15
17
  # Driving Codeman from inside a session
16
18
 
19
+ For **saved Codex conversation history**, use `codeman history read` and
20
+ [reference/history.md](reference/history.md). This is a read-only local command:
21
+ route to it **before §0**, including outside a managed session. It needs no API
22
+ URL, credentials or running server. All live API operations below retain §0.
23
+
17
24
  You are an agent running inside a Codeman-managed terminal session. Codeman is the
18
25
  server that spawned you; its HTTP API can start, prompt, watch, and delete other
19
26
  sessions.
@@ -30,21 +37,33 @@ through: the verbs in detail (the old §5) in [reference/verbs.md](reference/ver
30
37
  worked multi-worker flows in [reference/recipes.md](reference/recipes.md), endpoint
31
38
  tables and a symptom gallery in [reference/endpoints.md](reference/endpoints.md), and
32
39
  direct messaging to claude workers in [reference/messaging.md](reference/messaging.md).
40
+ Experimental bounded inspection and guarded stage/submit load only when needed from
41
+ [reference/inspection-and-staging.md](reference/inspection-and-staging.md).
33
42
 
34
43
  This skill works the same whichever CLI you are: Codeman puts it where each CLI looks
35
44
  for skills (`.claude/skills/` for Claude Code, `.agents/skills/` for Codex, Gemini CLI,
36
45
  OpenCode, Antigravity, Grok, DeepSeek and OMP). A **pi** or **shell** worker never gets
37
46
  it, but has the same environment: tell it *"other sessions: `codeman agent --help`"*.
38
- That bundled CLI (`ls`, `spawn`, `send`, `wait`, `read`, `interrupt`, `rm`, and the inbox
39
- verbs `post`, `inbox`, `ack`) is the same
47
+ That bundled CLI (`ls`, `spawn`, `send`, `wait`, `read`, `inspect`, `stage`, `submit`,
48
+ `interrupt`, `rm`, and the inbox verbs `post`, `inbox`, `ack`) is the same
40
49
  verbs over the same endpoints, with the guards below (no control bytes, no self-delete,
41
50
  no guessed URL) enforced in code, and it is also your own way through when this
42
51
  machine has no `jq` for the preamble below.
43
52
 
53
+ For an existing project, use one command:
54
+ `codeman agent spawn --working-dir /path/to/project --mode codex`. With no case and
55
+ no `--working-dir`, `spawn` uses cwd. Give editing workers separate worktrees. A
56
+ positional case keeps quick-start behavior and cannot be combined with
57
+ `--working-dir`; an empty `--working-dir` (`"$WT"` unset) refuses with exit 4
58
+ rather than falling back to cwd, and a cwd that is HOME refuses too (hooks would
59
+ land in `~/.claude`; pass `--working-dir ~` to mean it). Directory launches keep parent lineage and never
60
+ mark the project as agent scratch. If startup fails or is ambiguous, the CLI
61
+ reports the exact created ID: inspect it before retrying or cleaning up.
62
+
44
63
  ## 0. Guard and bootstrap
45
64
 
46
- If `CODEMAN_MUX` is not `1`, **stop and say so**. Do not guess an API URL; a server
47
- you are not part of is not yours to drive.
65
+ For live API operations, if `CODEMAN_MUX` is not `1`, **stop and say so**.
66
+ Do not guess an API URL; a server you are not part of is not yours to drive.
48
67
 
49
68
  Two failures come from your CLI, not from Codeman: name them to the user instead of
50
69
  retrying.
@@ -0,0 +1,84 @@
1
+ # Saved Codex history (offline)
2
+
3
+ Use `codeman history read` for retained local Codex JSONL transcripts. This command
4
+ works outside a managed session and never calls the HTTP API. Do not bootstrap
5
+ API credentials or require `CODEMAN_MUX` for this read-only job.
6
+
7
+ Choose exactly one selector:
8
+
9
+ ```bash
10
+ codeman history read --file /path/to/rollout.jsonl
11
+ codeman history read --session-id aaaaaaaa --sessions-root /path/to/sessions
12
+ codeman history read --root-session-id aaaaaaaa-1111-4111-8111-111111111111 \
13
+ --topic 'authentication' --after 2026-10-01 --before 2026-10-24 \
14
+ --max-bytes 65536
15
+ ```
16
+
17
+ `--session-id` accepts a native conversation UUID or unique prefix of at least
18
+ 8 characters; identity comes from metadata, not filenames. `--root-session-id`
19
+ requires a full native UUID and includes transitive descendants found in metadata
20
+ (spawned sub-agents and forks). The default store is `$CODEX_HOME/sessions`, or
21
+ `~/.codex/sessions` when unset; a store directory that is itself a symlink is
22
+ followed, links inside it are skipped. `--file` selects one file and does not
23
+ search for its ancestors or children.
24
+
25
+ Topic matching is a case-insensitive literal substring. Dates are inclusive
26
+ `YYYY-MM-DD` bounds compared as UTC calendar dates. Each filter selects whole
27
+ user/assistant turns; a turn is retained if it has a date match and a topic match,
28
+ which may be different messages in that turn. Tool calls/results and injected
29
+ context (rows Codex adds that start with `#` or `<`, such as AGENTS.md and
30
+ `<environment_context>`) are omitted. Adjacent duplicate native representations
31
+ are collapsed; repeated messages from the same representation are retained.
32
+
33
+ A forked or spawned thread's file starts with a copy of its parent's history. That
34
+ copy is dropped (it is in the parent's own file), so each record belongs to the
35
+ thread that wrote it. A later `session_meta` for any thread that is not a declared
36
+ ancestor still refuses the file.
37
+
38
+ JSON is the default (`--json` or `--format json`); `--format text` prints one block
39
+ per record. `--json` together with `--format text` is an error.
40
+ Results identify source paths, native session/root IDs, timestamps, roles and phases,
41
+ plus scan counters and `complete`. `phase` is Codex's own (`commentary` or
42
+ `final_answer`) on assistant records and `null` on user records or when none was
43
+ recorded. A root ID is `null` when ancestry cannot be proven.
44
+ Report that uncertainty; never relabel the queried child as its own root.
45
+ `complete: false` reports malformed or partial records (a running Codex may be
46
+ mid-write), skipped links, unattributed files, compressed rollouts, unreadable
47
+ directories, missing ancestors, or an inherited copy whose end could not be found
48
+ or proven (including when the parent's file is present but cannot be read). It
49
+ does not prove the store includes every conversation ever created, and a filtered
50
+ result is only evidence for its selected scope.
51
+
52
+ Reads require regular UTF8 files and reject final-component symlinks, conflicting
53
+ metadata, ambiguous IDs, cyclic ancestry and invalid selectors. A store file that
54
+ cannot be attributed (invalid UTF8 in its head, no metadata in its first 128 KiB,
55
+ changing while its head is read) is counted in `unattributedFiles` instead of
56
+ failing the lookup. Codex's optional rollout compression rewrites cold rollouts to
57
+ `*.jsonl.zst`; those are not read. Each is counted in `compressedFiles` (one whose
58
+ plain `.jsonl` sibling exists is skipped, as Codex skips it), and a lookup that
59
+ misses names them. A compressed file passed to `--file` is refused; read a
60
+ decompressed copy instead (`zstd -dc <file> > copy.jsonl`). A selected rollout
61
+ that a running Codex is appending to is read as a snapshot up to its last complete
62
+ line; one that was replaced or truncated fails. Parent directories of an explicitly supplied path must be trusted; this is
63
+ not a filesystem sandbox.
64
+
65
+ Budgets, each failing with an error rather than a partial answer. Every flag is
66
+ validated before the first file is read, and only plain decimal integers are
67
+ accepted:
68
+
69
+ | Flag | Bounds | Default | Maximum |
70
+ | ---- | ------ | ------- | ------- |
71
+ | `--metadata-bytes` | head reads attributing every rollout in the store | 1 GiB | 16 GiB |
72
+ | `--max-entries` | directory entries visited | 65536 | 1048576 |
73
+ | `--scan-bytes` | the selected transcripts, plus any parent file read to prove where a copy ends (each file charged once) | 16 MiB | 256 MiB |
74
+ | `--max-bytes` | output, including the final newline | 8192 | 1048576 |
75
+
76
+ Each rollout's `session_meta` carries Codex's base instructions (about 20 KiB), so
77
+ attributing a store costs about that much per rollout, charged to
78
+ `--metadata-bytes`, never to `--scan-bytes`. Directory depth is capped at 8 (Codex
79
+ writes `YYYY/MM/DD`). Output is never silently truncated or split into partial
80
+ JSON or turns. Narrow the store/filters or explicitly raise the relevant budget.
81
+ Missing evidence is an error, not an empty success.
82
+
83
+ This command does not inspect live panes, stage prompts, submit input or manage
84
+ sessions. For live operations return to the managed-session guard in the skill.
@@ -0,0 +1,152 @@
1
+ # Experimental bounded inspection and guarded submission
2
+
3
+ **Experimental:** `codeman agent inspect`, `stage` and `submit`, and their
4
+ `/api/v1/sessions/:id/inspect|stage|submit` routes (including `/api` aliases),
5
+ are labeled experimental and so are not part of the stable CLI/HTTP contract. Their commands, flags, paths,
6
+ payloads and behavior may change or be removed in a minor or patch release.
7
+ Pin a compatible Codeman version when automating them. Authentication and
8
+ session ownership checks still apply.
9
+
10
+ They are also opt-in: until the user turns on **Experimental agent coordination**
11
+ in App Settings (`agentCoordinationEnabled`, off by default), every one of them
12
+ fails with `FORBIDDEN: ... (HTTP 403)` naming that setting. That is not something
13
+ to retry or work around: tell the user, or use `send`/`read` instead.
14
+
15
+ Use these commands inside a Codeman session. They use its authenticated HTTP API.
16
+ They supplement `send`, `wait` and `read` over the same HTTP transport; no direct
17
+ tmux access is required.
18
+
19
+ ## Inspect recent evidence
20
+
21
+ ```bash
22
+ codeman agent inspect "$SID" --max-bytes 8192 --json
23
+ codeman agent inspect "$SID" --max-bytes 8192 --since "$CURSOR" --json
24
+ ```
25
+
26
+ Read `source`, `identity`, `activity`, `evidence`, `truncated` and `cursor` before
27
+ interpreting the result. `maxBytes` bounds UTF8 evidence bytes, excluding the JSON
28
+ metadata/envelope. The default is 8192, maximum 65536.
29
+
30
+ - `codex-jsonl`: strictly attributed local Codex JSONL records. The first call
31
+ selects recent complete records within a bounded tail window;
32
+ `omittedBeforeBytes` labels older bytes deliberately omitted. A cursor continues
33
+ after the last consumed complete record. Partial final records remain pending.
34
+ - `pane-snapshot`: UTF8-safe tail of a visible frame or the session's buffered
35
+ output. It has `incremental:false` and `cursor:null`. This is the fallback for
36
+ other modes, remote/container sessions, ambiguous attribution, and exhausted
37
+ attribution budgets. Repeating a snapshot can repeat evidence.
38
+
39
+ Attribution proves which file supplied the evidence, not which thread is now
40
+ active in the TUI. A launch resume that conflicts with a newly owned rollout
41
+ falls back to a snapshot. An in-CLI `/resume` of an existing foreign-originator
42
+ thread may leave launch metadata unchanged. Check `identity.conversationId`;
43
+ these reads do not provide authoritative current-thread tracking.
44
+
45
+ Terminal resizes preserve inspection cursors. Cursors are opaque, scoped to the
46
+ requesting identity, session, pane lifecycle generation
47
+ and attributed transcript, expire after 15 minutes, and disappear on server
48
+ restart. Each caller and session keeps its newest 8 (so retrying the last call
49
+ works), at most 1024 in all; an older cursor can be retired. A replaced/truncated/rewritten source, reassociated session, foreign cursor
50
+ or unavailable native source returns `409 CONFLICT`. Inspect again without
51
+ `--since` to establish a new tail baseline; this may omit older evidence.
52
+
53
+ `blockedReason` explains an unfinished or oversized record. Incremental reads
54
+ never split or silently skip one. Increase the byte budget for
55
+ `record-exceeds-output-budget`; when the record exceeds the maximum or scan cap,
56
+ use `read --tail` for a labeled terminal view or start a fresh tail baseline.
57
+ Inspect scans at most 256 KiB of record data plus bounded generation anchors.
58
+ Attribution separately caps directory entries (8192), depth (8), negative-filter
59
+ prefix reads (8 MiB) and candidate metadata including final verification (1 MiB). Evidence and
60
+ activity are observations, not proof of task completion or authorization.
61
+
62
+ ## Stage, inspect, then submit once
63
+
64
+ ```bash
65
+ printf '%s' 'Review src/ and report the failing tests' > /tmp/codeman-message.txt
66
+ codeman agent stage "$SID" --message-file /tmp/codeman-message.txt --json
67
+ # Review the returned stageId, sha256, expiresAt; inspect the target if needed.
68
+ codeman agent submit "$SID" --stage-id "$STAGE_ID" --confirm-sha "$SHA256" --json
69
+ ```
70
+
71
+ Stage types the file contents **without Enter**. The file must be regular, UTF8,
72
+ one printable line, at most 8192 bytes, with no control characters or trailing
73
+ whitespace/newline. It initially supports a local Codex composer with a recognized
74
+ footer and an exact, unwrapped line/caret capture. Before typing, stage checks
75
+ the actual captured width and composer indentation; a message that would reach
76
+ the right edge or wrap is refused without writing anything. Unknown, working, modal,
77
+ wrapped, remote/container and shell composers are refused. A customized footer or
78
+ unsupported character-width rendering can also make verification unavailable.
79
+ Self-targets are refused by the CLI.
80
+
81
+ Stage also refuses, before typing, a message that would change what Enter does:
82
+ one starting with `/` (a Codex slash command; with its command list open, Enter
83
+ would run the highlighted command instead) or `!` (a shell command), and one whose
84
+ **last** word starts with `@` (file/mention search) or `$` (skills). Those popups
85
+ follow the caret, which stage leaves at the end, so `look at @src/app.ts please`
86
+ is fine. Send slash and shell commands with `codeman agent send`, and end a
87
+ message with an ordinary word when it mentions a file. A frame showing a
88
+ suggestion popup above the composer is refused at stage and submit too.
89
+
90
+ The receipt stores a hash and ephemeral identities, never prompt content. It
91
+ expires after two minutes; capacity is 1024 live receipts. After typing, stage
92
+ re-reads the pane (every 50 ms, up to one second, since Codex repaints a little
93
+ after the keys) and issues a receipt only for a frame that shows exactly the
94
+ message with the caret after it. Any refusal after the keys went out (typing
95
+ failed or was ambiguous, the pane or input changed, or no frame verified in time)
96
+ says the message may already be in the composer: no receipt was issued. Do not
97
+ stage again on top of it; follow [Recover a stranded draft](#recover-a-stranded-draft) below first.
98
+
99
+ While a submit's Enter is in flight (milliseconds, plus the moment it takes to
100
+ write input held behind it), TAGGED input to that session (`clientId` and `seq`,
101
+ which `codeman agent send` and `interrupt` always carry) answers `409 CONFLICT`
102
+ with nothing written; retry it. Untagged input, terminal keystrokes, `send-key`
103
+ and Codeman's own managed writes are held instead and written in arrival order
104
+ once the Enter is out.
105
+
106
+ Submit checks the caller, session, hash, expiry, pane lifecycle, input revision,
107
+ geometry, exact composer and caret, then sends one conditional Enter at the session's input
108
+ boundary. It first waits until 250 ms have passed since stage typed (Codex turns an
109
+ Enter that follows typed text within 120 ms into a newline), so submitting right
110
+ after stage is fine. Competing Codeman writes are held during dispatch and written
111
+ after it, except tagged input, which is refused with `409`. Browser/API/key
112
+ input and managed restarts invalidate receipts even when the visible text or pid
113
+ is restored. Direct tmux edits are outside that input serialization; captures
114
+ remain evidence at the time observed, rather than terminal isolation.
115
+
116
+ Every owned submit attempt consumes its receipt, including hash mismatch,
117
+ verification failure and delivery failure. Concurrent/repeated submits cannot
118
+ replay Enter, and there is no autonomous retry. A successful response means one
119
+ Enter was delivered; use inspection or a suitable wait signal to observe native
120
+ acceptance. A `409` or broken connection means inspect first. Do not automatically
121
+ stage/submit again or send an unguarded Enter to recover.
122
+
123
+ ## Recover a stranded draft
124
+
125
+ A consumed receipt cannot be reused, and a stage refused after typing issued
126
+ none. First observe whether the prompt is still in the composer or was accepted: use transcript evidence/wait signals and a fresh terminal capture
127
+ (`GET /api/v1/sessions/:id/terminal?tail=8192`). If the target is working, has
128
+ accepted the prompt, shows a dialog, or delivery is ambiguous, keep observing;
129
+ do not clear or resend anything. A transcript prompt row alone is not proof
130
+ that text remains unsent in the current composer.
131
+
132
+ Only recover an unambiguously unsent draft in an input-ready local Codex
133
+ composer: verify the complete draft, recognized footer and caret at its end in
134
+ the current frame, and ensure no other writer can edit this target during
135
+ recovery. If the frame is wrapped, incomplete or unclear, ask the user to clear
136
+ it in the terminal instead of guessing keys. Never use `send` to overwrite a
137
+ draft: that appends and submits both prompts together.
138
+
139
+ After those checks, the known Codex line-clear chord may be sent once through
140
+ the existing raw input route, with **no Enter**. This conditional example uses
141
+ the authenticated `CURL`/`API` values loaded by §0 and the exact target id:
142
+
143
+ ```bash
144
+ "${CURL[@]}" -X POST "$API/api/v1/sessions/$SID/input" \
145
+ -H 'Content-Type: application/json' -d '{"input":"\u0015","useMux":false}'
146
+ ```
147
+
148
+ Do not retry a failed/ambiguous clear blindly. Read a fresh terminal frame and
149
+ verify the composer is empty and input-ready. Only then create a **fresh** stage
150
+ receipt for the intended prompt and submit that receipt once. If the draft did
151
+ not clear, the target began working, or verification remains uncertain, stop and
152
+ ask the user; do not send an unguarded Enter or replay the old prompt.
@@ -48,7 +48,7 @@ own head: the worker enforcing the cap is the one who has to be told about it.
48
48
 
49
49
  | Job | Channel |
50
50
  | --- | --- |
51
- | spawn a worker, create its case | HTTP `quick-start` (the only path) |
51
+ | spawn a worker, create its case | HTTP `quick-start` (the only path that creates a case); an existing directory is `POST sessions` + `interactive` (`codeman agent spawn --working-dir`) |
52
52
  | readiness, incl. the trust dialog | HTTP, Flow 1 (a message cannot answer a dialog) |
53
53
  | deliver a task to a READY claude worker | **messaging** (preferred) or HTTP input |
54
54
  | steer a BUSY claude worker mid-turn | **messaging** (read between the worker's tool calls; the HTTP path can only type into the composer, where text waits for the turn to end) |
@@ -31,7 +31,7 @@ wrong directory.** `quick-start` with a new `caseName` does not find your repo:
31
31
  |-------------------|------|------------------------------|
32
32
  | a fresh scratch dir (throwaway experiments) | `POST /api/v1/quick-start {"caseName":"scratch-1","mode":"claude"}` with a **new** case name | Codeman creates the directory and **writes hooks**: `stop` and `blocked` fire, send-and-wait is trustworthy |
33
33
  | a linked case (a real repo in the linked-cases registry) | same call with the linked name | **hooks installed at session create**, so `stop` fires here too. Not guaranteed: the operator can turn it off. Check |
34
- | any other absolute path, e.g. a git worktree you made | `POST /api/v1/sessions {"workingDir":"/abs/path","mode":"claude"}` then `POST /api/v1/sessions/:id/interactive` | same: **hooks installed at session create**, subject to the same setting. Check |
34
+ | any other absolute path, e.g. a git worktree you made | `POST /api/v1/sessions {"workingDir":"/abs/path","mode":"claude"}` then `POST /api/v1/sessions/:id/interactive` (what `codeman agent spawn --working-dir /abs/path` sends) | same: **hooks installed at session create**, subject to the same setting. Check |
35
35
 
36
36
  Read `.data.casePath` back from the `quick-start` response and check it is where you
37
37
  meant. `caseName` accepts letters, digits, `-` and `_` only, and it resolves through
@@ -550,8 +550,10 @@ this (`src/web/routes/approval-routes.ts:43`). ESC is then claude's own interrup
550
550
  that half is the CLI's behavior, not something this API guarantees.
551
551
 
552
552
  - **This is not the composer-clearing tool.** Esc (and Ctrl+U) do **not** clear a
553
- typed-but-unsubmitted prompt, verified live. The only recovery there is to submit it
554
- with `{"input":"\r"}` and let the worker read the junk line.
553
+ typed-but-unsubmitted Claude prompt, verified live. Do not submit an unverified
554
+ draft merely to clear it. For a refused guarded local Codex stage or submit, use the
555
+ verified-unsent recovery in [inspection and staging](inspection-and-staging.md#recover-a-stranded-draft);
556
+ when the composer is unclear, ask the user to clear it in the terminal.
555
557
  - The interrupted turn already burned its tokens. Interrupting early saves the rest.
556
558
  - `POST /api/sessions/:id/send-key` is a different endpoint and cannot do this: its
557
559
  allowlist is S-Enter / C-Enter only.
@@ -811,3 +813,16 @@ Rules that matter:
811
813
  not parked, `send` it one line ("read your inbox: `codeman agent inbox`").
812
814
  - **Lifetime.** An inbox goes with its session on delete. A server restart empties all
813
815
  inboxes unless the user turned on *Keep agent inbox across restarts* in Settings.
816
+
817
+ ## Experimental bounded inspection and guarded submission
818
+
819
+ These three commands and their HTTP routes are labeled experimental, so they may
820
+ change or be removed in minor or patch releases. They are also opt-in
821
+ (`agentCoordinationEnabled`, off by default: a 403 naming it until the user turns
822
+ it on). Use `codeman agent inspect` for bounded evidence and incremental local
823
+ Codex cursors.
824
+ Use `codeman agent stage --message-file` followed by one hash-confirmed
825
+ `codeman agent submit --stage-id --confirm-sha` when the exact local Codex
826
+ composer and caret can be verified. Other targets provide labeled snapshots or
827
+ refuse guarded input. See [inspection and staging](inspection-and-staging.md)
828
+ for budgets, receipt expiry, conflict handling and supported composers.