aicodeman 1.45.0 → 1.47.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 (312) 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/ralph-fix-plan-watcher.d.ts +13 -1
  87. package/dist/ralph-fix-plan-watcher.d.ts.map +1 -1
  88. package/dist/ralph-fix-plan-watcher.js +21 -3
  89. package/dist/ralph-fix-plan-watcher.js.map +1 -1
  90. package/dist/ralph-tracker.d.ts +6 -0
  91. package/dist/ralph-tracker.d.ts.map +1 -1
  92. package/dist/ralph-tracker.js +8 -0
  93. package/dist/ralph-tracker.js.map +1 -1
  94. package/dist/session.d.ts +113 -0
  95. package/dist/session.d.ts.map +1 -1
  96. package/dist/session.js +341 -13
  97. package/dist/session.js.map +1 -1
  98. package/dist/terminal-input-classifier.d.ts +8 -0
  99. package/dist/terminal-input-classifier.d.ts.map +1 -0
  100. package/dist/terminal-input-classifier.js +60 -0
  101. package/dist/terminal-input-classifier.js.map +1 -0
  102. package/dist/tmux-manager.d.ts +23 -1
  103. package/dist/tmux-manager.d.ts.map +1 -1
  104. package/dist/tmux-manager.js +54 -6
  105. package/dist/tmux-manager.js.map +1 -1
  106. package/dist/types/session.d.ts +15 -0
  107. package/dist/types/session.d.ts.map +1 -1
  108. package/dist/types/session.js.map +1 -1
  109. package/dist/web/network-auth-policy.d.ts +13 -3
  110. package/dist/web/network-auth-policy.d.ts.map +1 -1
  111. package/dist/web/network-auth-policy.js +15 -3
  112. package/dist/web/network-auth-policy.js.map +1 -1
  113. package/dist/web/ports/infra-port.d.ts +2 -0
  114. package/dist/web/ports/infra-port.d.ts.map +1 -1
  115. package/dist/web/public/admin-ui.js.gz +0 -0
  116. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  117. package/dist/web/public/app.fe8a6b4c.js +43 -0
  118. package/dist/web/public/app.fe8a6b4c.js.br +0 -0
  119. package/dist/web/public/app.fe8a6b4c.js.gz +0 -0
  120. package/dist/web/public/approvals-ui.js.gz +0 -0
  121. package/dist/web/public/{constants.0b3cdde9.js → constants.affcdde6.js} +611 -12
  122. package/dist/web/public/constants.affcdde6.js.br +0 -0
  123. package/dist/web/public/constants.affcdde6.js.gz +0 -0
  124. package/dist/web/public/cron-ui.js.gz +0 -0
  125. package/dist/web/public/entrance-animations.js.gz +0 -0
  126. package/dist/web/public/git-status-ui.js +88 -5
  127. package/dist/web/public/git-status-ui.js.br +0 -0
  128. package/dist/web/public/git-status-ui.js.gz +0 -0
  129. package/dist/web/public/header-divider.eb2d2463.js.gz +0 -0
  130. package/dist/web/public/home-sessions.js.gz +0 -0
  131. package/dist/web/public/host-wake-ui.js.gz +0 -0
  132. package/dist/web/public/i18n.a618ef4e.js +2 -0
  133. package/dist/web/public/i18n.a618ef4e.js.br +0 -0
  134. package/dist/web/public/i18n.a618ef4e.js.gz +0 -0
  135. package/dist/web/public/{image-input.941386e7.js → image-input.a5d0f8f3.js} +18 -9
  136. package/dist/web/public/image-input.a5d0f8f3.js.br +0 -0
  137. package/dist/web/public/image-input.a5d0f8f3.js.gz +0 -0
  138. package/dist/web/public/index.html +110 -19
  139. package/dist/web/public/index.html.br +0 -0
  140. package/dist/web/public/index.html.gz +0 -0
  141. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  142. package/dist/web/public/{keyboard-accessory.4e8b20ac.js → keyboard-accessory.ff64fafc.js} +5 -1
  143. package/dist/web/public/keyboard-accessory.ff64fafc.js.br +0 -0
  144. package/dist/web/public/keyboard-accessory.ff64fafc.js.gz +0 -0
  145. package/dist/web/public/{mobile-handlers.1c0cbcd8.js → mobile-handlers.f7c3f692.js} +22 -0
  146. package/dist/web/public/mobile-handlers.f7c3f692.js.br +0 -0
  147. package/dist/web/public/mobile-handlers.f7c3f692.js.gz +0 -0
  148. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  149. package/dist/web/public/mobile-overview.js.gz +0 -0
  150. package/dist/web/public/mobile.1bbd2d67.css +1 -0
  151. package/dist/web/public/mobile.1bbd2d67.css.br +0 -0
  152. package/dist/web/public/mobile.1bbd2d67.css.gz +0 -0
  153. package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
  154. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  155. package/dist/web/public/{panels-ui.78c3defb.js → panels-ui.4a56ee63.js} +1 -1
  156. package/dist/web/public/panels-ui.4a56ee63.js.br +0 -0
  157. package/dist/web/public/panels-ui.4a56ee63.js.gz +0 -0
  158. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  159. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  160. package/dist/web/public/readmymind-ui.js.gz +0 -0
  161. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  162. package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
  163. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  164. package/dist/web/public/session-lineage.js.gz +0 -0
  165. package/dist/web/public/{session-ui.ea6b1994.js → session-ui.426fb671.js} +8 -8
  166. package/dist/web/public/session-ui.426fb671.js.br +0 -0
  167. package/dist/web/public/session-ui.426fb671.js.gz +0 -0
  168. package/dist/web/public/{settings-ui.085dd202.js → settings-ui.a191154e.js} +11 -11
  169. package/dist/web/public/settings-ui.a191154e.js.br +0 -0
  170. package/dist/web/public/settings-ui.a191154e.js.gz +0 -0
  171. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  172. package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
  173. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  174. package/dist/web/public/styles.4f0c67a0.css +1 -0
  175. package/dist/web/public/styles.4f0c67a0.css.br +0 -0
  176. package/dist/web/public/styles.4f0c67a0.css.gz +0 -0
  177. package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
  178. package/dist/web/public/sw.js +2 -2
  179. package/dist/web/public/sw.js.br +0 -0
  180. package/dist/web/public/sw.js.gz +0 -0
  181. package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
  182. package/dist/web/public/{tab-rail-resize.7157e0bd.js → tab-rail-resize.b5cdceaf.js} +1 -1
  183. package/dist/web/public/tab-rail-resize.b5cdceaf.js.br +0 -0
  184. package/dist/web/public/tab-rail-resize.b5cdceaf.js.gz +0 -0
  185. package/dist/web/public/terminal-keycode229-recovery.98843069.js +1 -0
  186. package/dist/web/public/terminal-keycode229-recovery.98843069.js.br +0 -0
  187. package/dist/web/public/terminal-keycode229-recovery.98843069.js.gz +0 -0
  188. package/dist/web/public/terminal-split.js.gz +0 -0
  189. package/dist/web/public/terminal-tile.js +10 -6
  190. package/dist/web/public/terminal-tile.js.br +0 -0
  191. package/dist/web/public/terminal-tile.js.gz +0 -0
  192. package/dist/web/public/terminal-ui.7e787829.js +4 -0
  193. package/dist/web/public/terminal-ui.7e787829.js.br +0 -0
  194. package/dist/web/public/terminal-ui.7e787829.js.gz +0 -0
  195. package/dist/web/public/tile-grid.js.gz +0 -0
  196. package/dist/web/public/ultracode-panel.js.gz +0 -0
  197. package/dist/web/public/ultracode-windows.js.gz +0 -0
  198. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  199. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  200. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  201. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  202. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  203. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  204. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  205. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  206. package/dist/web/public/vendor/{xterm-predictive-echo.ae5e3d5d.js → xterm-predictive-echo.276b11d9.js} +1 -1
  207. package/dist/web/public/vendor/xterm-predictive-echo.276b11d9.js.br +0 -0
  208. package/dist/web/public/vendor/xterm-predictive-echo.276b11d9.js.gz +0 -0
  209. package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js +5 -0
  210. package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js.br +0 -0
  211. package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js.gz +0 -0
  212. package/dist/web/public/vendor/xterm.css.gz +0 -0
  213. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  214. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  215. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  216. package/dist/web/public/webview-tabs.js.gz +0 -0
  217. package/dist/web/public/whats-new.json +8 -9
  218. package/dist/web/request-body-parser.d.ts +23 -0
  219. package/dist/web/request-body-parser.d.ts.map +1 -0
  220. package/dist/web/request-body-parser.js +57 -0
  221. package/dist/web/request-body-parser.js.map +1 -0
  222. package/dist/web/routes/agent-session-routes.d.ts +20 -0
  223. package/dist/web/routes/agent-session-routes.d.ts.map +1 -0
  224. package/dist/web/routes/agent-session-routes.js +160 -0
  225. package/dist/web/routes/agent-session-routes.js.map +1 -0
  226. package/dist/web/routes/case-routes.d.ts.map +1 -1
  227. package/dist/web/routes/case-routes.js +32 -26
  228. package/dist/web/routes/case-routes.js.map +1 -1
  229. package/dist/web/routes/config-backup-routes.js +2 -2
  230. package/dist/web/routes/config-backup-routes.js.map +1 -1
  231. package/dist/web/routes/file-routes.d.ts +5 -0
  232. package/dist/web/routes/file-routes.d.ts.map +1 -1
  233. package/dist/web/routes/file-routes.js +5 -1
  234. package/dist/web/routes/file-routes.js.map +1 -1
  235. package/dist/web/routes/git-status-routes.d.ts +21 -7
  236. package/dist/web/routes/git-status-routes.d.ts.map +1 -1
  237. package/dist/web/routes/git-status-routes.js +78 -12
  238. package/dist/web/routes/git-status-routes.js.map +1 -1
  239. package/dist/web/routes/index.d.ts +1 -0
  240. package/dist/web/routes/index.d.ts.map +1 -1
  241. package/dist/web/routes/index.js +1 -0
  242. package/dist/web/routes/index.js.map +1 -1
  243. package/dist/web/routes/ralph-routes.d.ts.map +1 -1
  244. package/dist/web/routes/ralph-routes.js +52 -18
  245. package/dist/web/routes/ralph-routes.js.map +1 -1
  246. package/dist/web/routes/session-routes.d.ts.map +1 -1
  247. package/dist/web/routes/session-routes.js +101 -28
  248. package/dist/web/routes/session-routes.js.map +1 -1
  249. package/dist/web/routes/system-routes.d.ts.map +1 -1
  250. package/dist/web/routes/system-routes.js +87 -59
  251. package/dist/web/routes/system-routes.js.map +1 -1
  252. package/dist/web/routes/ws-routes.d.ts +5 -0
  253. package/dist/web/routes/ws-routes.d.ts.map +1 -1
  254. package/dist/web/routes/ws-routes.js +84 -47
  255. package/dist/web/routes/ws-routes.js.map +1 -1
  256. package/dist/web/schemas.d.ts +30 -7
  257. package/dist/web/schemas.d.ts.map +1 -1
  258. package/dist/web/schemas.js +40 -1
  259. package/dist/web/schemas.js.map +1 -1
  260. package/dist/web/server.d.ts +5 -2
  261. package/dist/web/server.d.ts.map +1 -1
  262. package/dist/web/server.js +102 -18
  263. package/dist/web/server.js.map +1 -1
  264. package/dist/web/settings-writer.d.ts +47 -0
  265. package/dist/web/settings-writer.d.ts.map +1 -0
  266. package/dist/web/settings-writer.js +110 -0
  267. package/dist/web/settings-writer.js.map +1 -0
  268. package/package.json +1 -1
  269. package/skills/codeman/SKILL.md +25 -6
  270. package/skills/codeman/reference/history.md +84 -0
  271. package/skills/codeman/reference/inspection-and-staging.md +152 -0
  272. package/skills/codeman/reference/messaging.md +1 -1
  273. package/skills/codeman/reference/verbs.md +18 -3
  274. package/dist/web/public/app.42d0abae.js +0 -43
  275. package/dist/web/public/app.42d0abae.js.br +0 -0
  276. package/dist/web/public/app.42d0abae.js.gz +0 -0
  277. package/dist/web/public/constants.0b3cdde9.js.br +0 -0
  278. package/dist/web/public/constants.0b3cdde9.js.gz +0 -0
  279. package/dist/web/public/i18n.1e68220d.js +0 -2
  280. package/dist/web/public/i18n.1e68220d.js.br +0 -0
  281. package/dist/web/public/i18n.1e68220d.js.gz +0 -0
  282. package/dist/web/public/image-input.941386e7.js.br +0 -0
  283. package/dist/web/public/image-input.941386e7.js.gz +0 -0
  284. package/dist/web/public/keyboard-accessory.4e8b20ac.js.br +0 -0
  285. package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
  286. package/dist/web/public/mobile-handlers.1c0cbcd8.js.br +0 -0
  287. package/dist/web/public/mobile-handlers.1c0cbcd8.js.gz +0 -0
  288. package/dist/web/public/mobile.12fb18de.css +0 -1
  289. package/dist/web/public/mobile.12fb18de.css.br +0 -0
  290. package/dist/web/public/mobile.12fb18de.css.gz +0 -0
  291. package/dist/web/public/panels-ui.78c3defb.js.br +0 -0
  292. package/dist/web/public/panels-ui.78c3defb.js.gz +0 -0
  293. package/dist/web/public/session-ui.ea6b1994.js.br +0 -0
  294. package/dist/web/public/session-ui.ea6b1994.js.gz +0 -0
  295. package/dist/web/public/settings-ui.085dd202.js.br +0 -0
  296. package/dist/web/public/settings-ui.085dd202.js.gz +0 -0
  297. package/dist/web/public/styles.3b59b41d.css +0 -1
  298. package/dist/web/public/styles.3b59b41d.css.br +0 -0
  299. package/dist/web/public/styles.3b59b41d.css.gz +0 -0
  300. package/dist/web/public/tab-rail-resize.7157e0bd.js.br +0 -0
  301. package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
  302. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js +0 -1
  303. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.br +0 -0
  304. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  305. package/dist/web/public/terminal-ui.fb2a27f5.js +0 -2
  306. package/dist/web/public/terminal-ui.fb2a27f5.js.br +0 -0
  307. package/dist/web/public/terminal-ui.fb2a27f5.js.gz +0 -0
  308. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.br +0 -0
  309. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  310. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js +0 -4
  311. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.br +0 -0
  312. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
@@ -78,7 +78,7 @@ import { createRequire } from 'node:module';
78
78
  import { RunSummaryTracker } from '../run-summary.js';
79
79
  import { OrchestratorLoop } from '../orchestrator-loop.js';
80
80
  import { getLifecycleLog } from '../session-lifecycle-log.js';
81
- import { applyWorkspaceHooks, pruneAgentSessionPreambles, readKeepWarmSettingsOrThrow, readPlanUsageTelemetryEnabled, removeAgentSessionPreamble, } from '../hooks-config.js';
81
+ import { applyWorkspaceHooks, pruneAgentSessionPreambles, readGitFetchSettings, readKeepWarmSettingsOrThrow, readPlanUsageTelemetryEnabled, removeAgentSessionPreamble, } from '../hooks-config.js';
82
82
  import { PushSubscriptionStore } from '../push-store.js';
83
83
  import webpush from 'web-push';
84
84
  import { SseStreamManager } from './sse-stream-manager.js';
@@ -93,6 +93,10 @@ import { AI_CHECK_MODEL } from '../config/ai-defaults.js';
93
93
  import { approvalInbox } from './approval-inbox.js';
94
94
  import { SCREEN_BLOCKED_SOURCE } from './screen-blocked.js';
95
95
  import { PromptCacheKeepWarmService } from '../prompt-cache-keepwarm.js';
96
+ import { fetchWorkspaceOnce } from '../git-fetch.js';
97
+ import { GitFetchScheduler } from '../git-fetch-scheduler.js';
98
+ import { probePathKind } from '../utils/bounded-path-probe.js';
99
+ import { defaultDockerWorkspaces } from './routes/git-status-routes.js';
96
100
  import { composerIsEmpty, plainFrame } from '../session-submit-verifier.js';
97
101
  import { detectUsageLimitPause } from '../usage-limit-patterns.js';
98
102
  import { stopDeepSeekWeb } from '../deepseek-web-server.js';
@@ -120,6 +124,7 @@ import { MAX_PASTE_IMAGE_BYTES } from '../config/buffer-limits.js';
120
124
  import { resolveTerminalHistoryConfig } from '../config/terminal-history.js';
121
125
  import { SseEvent } from './sse-events.js';
122
126
  import { MAX_ROUTE_PARAM_LENGTH } from './route-helpers.js';
127
+ import { mutateSettingsFile } from './settings-writer.js';
123
128
  import { getLatestPlanUsage, setLatestCodexPlanUsage, setLatestCopilotPlanUsage } from './plan-usage-latest.js';
124
129
  import { telemetrySignature } from '../usage-telemetry.js';
125
130
  import { readCodexPlanUsage, resolveCodexBinaryPath } from '../utils/codex-cli-resolver.js';
@@ -129,8 +134,9 @@ import { registerAuthMiddleware, registerSecurityHeaders, registerHostGuard, isL
129
134
  import { isMultiUserMode } from '../config/multiuser.js';
130
135
  import { bootstrapInitialAdmin, hasUsers, resolveClaudeModeForUsername } from '../user-store.js';
131
136
  import { installRouteErrorHandler } from './route-error-handler.js';
137
+ import { installEmptyApiPostParser } from './request-body-parser.js';
132
138
  import { isExplicitlyEnabled, isLoopbackBindHost, buildHostPolicy } from './network-auth-policy.js';
133
- import { registerPushRoutes, registerTeamRoutes, registerMuxRoutes, registerFileRoutes, registerHtmlViewRoutes, registerScheduledRoutes, registerHookEventRoutes, registerApprovalRoutes, registerInboxRoutes, registerRebootRestoreRoutes, registerReadMyMindRoutes, registerGitStatusRoutes, registerStatusTelemetryRoutes, registerSystemRoutes, registerCaseRoutes, registerWorktreeRoutes, registerSessionRoutes, registerRespawnRoutes, registerRalphRoutes, registerPlanRoutes, registerClipboardRoutes, registerSearchRoutes, registerOrchestratorRoutes, registerCronRoutes, registerMeRoutes, registerAdminRoutes, registerWsRoutes, registerVoiceRoutes, registerWebviewRoutes, registerTabLayoutRoutes, registerMcpSyncRoutes, registerConfigBackupRoutes, runConfigBackup, registerWebhookRoutes, registerDoctorRoutes, registerCustomModelRoutes, refreshAllCustomModelHosts, readCustomModelEndpointsEnabled, closeAllLlamaSwapLogTails, detectCustomModelSwapDisplacements, pruneIdleLlamaSwapLogTails, tryWebviewRefererFallback, registerCliRegistryRoutes, } from './routes/index.js';
139
+ import { registerPushRoutes, registerTeamRoutes, registerMuxRoutes, registerFileRoutes, registerHtmlViewRoutes, registerScheduledRoutes, registerHookEventRoutes, registerApprovalRoutes, registerInboxRoutes, registerRebootRestoreRoutes, registerReadMyMindRoutes, registerGitStatusRoutes, registerStatusTelemetryRoutes, registerSystemRoutes, registerCaseRoutes, registerWorktreeRoutes, registerSessionRoutes, registerAgentSessionRoutes, registerRespawnRoutes, registerRalphRoutes, registerPlanRoutes, registerClipboardRoutes, registerSearchRoutes, registerOrchestratorRoutes, registerCronRoutes, registerMeRoutes, registerAdminRoutes, registerWsRoutes, registerVoiceRoutes, registerWebviewRoutes, registerTabLayoutRoutes, registerMcpSyncRoutes, registerConfigBackupRoutes, runConfigBackup, registerWebhookRoutes, registerDoctorRoutes, registerCustomModelRoutes, refreshAllCustomModelHosts, readCustomModelEndpointsEnabled, closeAllLlamaSwapLogTails, detectCustomModelSwapDisplacements, pruneIdleLlamaSwapLogTails, tryWebviewRefererFallback, registerCliRegistryRoutes, } from './routes/index.js';
134
140
  import { isLostWebviewFrameNavigation } from './webview-proxy.js';
135
141
  import { CronService } from '../cron/cron-service.js';
136
142
  import { caseSessionSettled } from '../case-rename.js';
@@ -168,7 +174,7 @@ function escapeHtmlText(value) {
168
174
  export function escapeScriptJson(json) {
169
175
  return json.replace(/</g, '\\u003c');
170
176
  }
171
- import { SESSIONS_LIST_CACHE_TTL, SCHEDULED_CLEANUP_INTERVAL, SCHEDULED_RUN_MAX_AGE, SSE_HEARTBEAT_INTERVAL, SESSION_LIMIT_WAIT_MS, ITERATION_PAUSE_MS, STATS_COLLECTION_INTERVAL_MS, INACTIVITY_TIMEOUT_MS, CRON_TICK_INTERVAL, } from '../config/server-timing.js';
177
+ import { SESSIONS_LIST_CACHE_TTL, SCHEDULED_CLEANUP_INTERVAL, SCHEDULED_RUN_MAX_AGE, SSE_HEARTBEAT_INTERVAL, SESSION_LIMIT_WAIT_MS, ITERATION_PAUSE_MS, STATS_COLLECTION_INTERVAL_MS, INACTIVITY_TIMEOUT_MS, CRON_TICK_INTERVAL, GIT_FETCH_TICK_INTERVAL_MS, } from '../config/server-timing.js';
172
178
  /**
173
179
  * Get or generate a self-signed TLS certificate for HTTPS.
174
180
  * Certs are stored in ~/.codeman/certs/ and reused across restarts.
@@ -254,6 +260,38 @@ export class WebServer extends EventEmitter {
254
260
  },
255
261
  log: (message) => console.log(message),
256
262
  });
263
+ // Git fetch on an interval (git-fetch-scheduler.ts): the due-tracking lives in the service; the server
264
+ // lends it the facts a decision needs and stores what it reports on the session. A field initializer for
265
+ // the same reason as the keep-warm one above, and the scheduler reads the app setting FRESH on every tick:
266
+ // that is what makes a live toggle take effect on the next tick, and what makes turning it off actually
267
+ // stop the fetching. Default OFF, so an install that never enabled it spawns nothing at all.
268
+ gitFetchScheduler = new GitFetchScheduler({
269
+ sessionIds: () => this.sessions.keys(),
270
+ getSession: (id) => this.sessions.get(id),
271
+ readSettings: readGitFetchSettings,
272
+ // The same Docker list the panel's Fetch button uses: the rule has to be one rule. It rejects on a list
273
+ // that cannot be read, and the tick then fetches nothing (fail closed).
274
+ dockerWorkspaces: defaultDockerWorkspaces,
275
+ // Bounded probe, DEFAULT options (rule #516): a tick is a bulk scan, so it keeps the stall cap and must
276
+ // never be handed `pastCap`. A folder on a mount that stopped answering is skipped, not fetched.
277
+ probePath: (path) => probePathKind(path),
278
+ // Through the join, so the tick and the panel's Fetch button are the same run: a click on a folder the
279
+ // tick is already fetching joins it rather than starting a second `git fetch` in the same clone, and the
280
+ // run that gets published is the one that ran, with its own trigger.
281
+ fetch: (cwd, opts, trigger) => fetchWorkspaceOnce(cwd, opts, trigger),
282
+ publish: (id, status) => {
283
+ const session = this.sessions.get(id);
284
+ if (!session?.setGitFetchStatus(status))
285
+ return;
286
+ // No new SSE event: the status rides the session state the page already listens for, and the Git
287
+ // panel polls on its own clock. It is written to disk with the rest of the record, but the restore
288
+ // copies only the fields its own config lists and this is not one of them (src/types/session.ts), so
289
+ // what a run found is the panel's truth for the life of the session, not across a restart.
290
+ this.persistSessionState(session);
291
+ this.broadcast(SseEvent.SessionUpdated, this.getSessionStateWithRespawn(session));
292
+ },
293
+ log: (message) => console.log(message),
294
+ });
257
295
  /**
258
296
  * Wake-on-LAN registry, returned by `registerSessionRoutes`. Held for its LIFETIME
259
297
  * only — `drop()` on session cleanup, `stop()` on shutdown. Waking from here would
@@ -319,12 +357,14 @@ export class WebServer extends EventEmitter {
319
357
  windowTitle;
320
358
  indexHtmlTemplate;
321
359
  allowUnauthenticatedNetwork;
360
+ /** `--allow-unauthenticated-tunnel`; the tunnel guard reads it through the route context (#706). */
361
+ allowUnauthenticatedTunnel;
322
362
  _pasteImageGcStop = null;
323
363
  _eventLoopMonitor = null;
324
364
  /** Opt-in hooks-only listener on the docker bridge gateway (CODEMAN_DOCKER_BRIDGE_HOOKS). */
325
365
  _dockerBridgeServer = null;
326
366
  teamWatcherHandlers = null;
327
- constructor(port = 3000, https = false, testMode = false, host = '127.0.0.1', titleHostname, allowUnauthenticatedNetwork = false, basePath = '') {
367
+ constructor(port = 3000, https = false, testMode = false, host = '127.0.0.1', titleHostname, allowUnauthenticatedNetwork = false, basePath = '', allowUnauthenticatedTunnel = false) {
328
368
  super();
329
369
  this.setMaxListeners(0);
330
370
  this.host = host;
@@ -335,6 +375,7 @@ export class WebServer extends EventEmitter {
335
375
  this.testMode = testMode;
336
376
  this.allowUnauthenticatedNetwork =
337
377
  allowUnauthenticatedNetwork || isExplicitlyEnabled(process.env.CODEMAN_ALLOW_UNAUTHENTICATED_NETWORK);
378
+ this.allowUnauthenticatedTunnel = allowUnauthenticatedTunnel;
338
379
  this.titleHostname = titleHostname || getHostname();
339
380
  this.windowTitle = `codeman:${this.titleHostname}`;
340
381
  this.indexHtmlTemplate = readFileSync(join(__dirname, 'public', 'index.html'), 'utf-8');
@@ -692,6 +733,7 @@ export class WebServer extends EventEmitter {
692
733
  scheduledRuns: this.scheduledRuns,
693
734
  teamWatcher: this.teamWatcher,
694
735
  tunnelManager: this.tunnelManager,
736
+ allowUnauthenticatedTunnel: this.allowUnauthenticatedTunnel,
695
737
  pushStore: this.pushStore,
696
738
  startScheduledRun: this.startScheduledRun.bind(this),
697
739
  stopScheduledRun: this.stopScheduledRun.bind(this),
@@ -776,6 +818,8 @@ export class WebServer extends EventEmitter {
776
818
  this.userFailures = authState.userFailures;
777
819
  this.userIpFailures = authState.userIpFailures;
778
820
  }
821
+ // Bodyless API POSTs may arrive as empty chunked streams through a proxy.
822
+ installEmptyApiPostParser(this.app);
779
823
  // WebSocket support (terminal I/O — low-latency bidirectional channel)
780
824
  await this.app.register(fastifyWebsocket);
781
825
  // Multipart parsing (used by paste-image). Replaces a hand-rolled
@@ -1042,6 +1086,17 @@ export class WebServer extends EventEmitter {
1042
1086
  registerRebootRestoreRoutes(this.app, ctx);
1043
1087
  registerReadMyMindRoutes(this.app, ctx);
1044
1088
  registerGitStatusRoutes(this.app, ctx);
1089
+ // Git fetch on an interval: this is only the wake-up, the scheduler itself decides what is due. Off in
1090
+ // tests for the same reason as the polls below: a test drives `tick()` directly instead of waiting on
1091
+ // a timer the harness would have to keep alive. The setting is read FRESH inside `tick()`, so turning
1092
+ // it on takes effect on the next tick and turning it off actually stops the fetching.
1093
+ if (!this.testMode) {
1094
+ this.cleanup.setInterval(() => {
1095
+ this.gitFetchScheduler.tick().catch((err) => {
1096
+ console.error('[git fetch] interval tick failed:', getErrorMessage(err));
1097
+ });
1098
+ }, GIT_FETCH_TICK_INTERVAL_MS, { description: 'git fetch interval checker' });
1099
+ }
1045
1100
  registerStatusTelemetryRoutes(this.app, ctx);
1046
1101
  registerSystemRoutes(this.app, ctx);
1047
1102
  registerCaseRoutes(this.app, ctx);
@@ -1049,6 +1104,7 @@ export class WebServer extends EventEmitter {
1049
1104
  // The registry's lifetime is the server's: it drops per-session wake state on every
1050
1105
  // cleanup path and resolves in-flight wakes on shutdown.
1051
1106
  this.remoteWake = registerSessionRoutes(this.app, ctx);
1107
+ registerAgentSessionRoutes(this.app, ctx);
1052
1108
  registerRespawnRoutes(this.app, ctx);
1053
1109
  registerRalphRoutes(this.app, ctx);
1054
1110
  registerPlanRoutes(this.app, ctx);
@@ -1272,6 +1328,9 @@ export class WebServer extends EventEmitter {
1272
1328
  async _doCleanupSession(sessionId, killMux, reason, coordinateLayout = true) {
1273
1329
  const session = this.sessions.get(sessionId);
1274
1330
  this.keepWarmService.remove(sessionId);
1331
+ // A closed session takes its place in the fetch schedule with it: otherwise a detached-and-forgotten id
1332
+ // would still count as tracked, and its next run would land on a session that no longer exists.
1333
+ this.gitFetchScheduler.remove(sessionId);
1275
1334
  const pinned = session?.pinned === true || this.store.getSession(sessionId)?.pinned === true;
1276
1335
  if (coordinateLayout && session && killMux && !pinned) {
1277
1336
  return this.tabLayouts.runSessionDeletion([{ id: sessionId, owner: session.owner }], () => this._doCleanupSession(sessionId, killMux, reason, false));
@@ -1645,6 +1704,13 @@ export class WebServer extends EventEmitter {
1645
1704
  .filter((entry) => entry.capabilities.transcriptPageKeys === true)
1646
1705
  .map((entry) => entry.id);
1647
1706
  html = html.replace('</head>', () => `<script>window.__codemanTranscriptPageKeys=${JSON.stringify(pageKeyClis)};</script>\n</head>`);
1707
+ // Which run modes keep their PTY rows under a phone keyboard (inline renderers,
1708
+ // the `keyboardHold` capability; `_keyboardHoldEligible` in terminal-ui.js). Same
1709
+ // shape and reasoning as the page-key list above.
1710
+ const keyboardHoldClis = enabledClis()
1711
+ .filter((entry) => entry.capabilities.keyboardHold === 'inline')
1712
+ .map((entry) => entry.id);
1713
+ html = html.replace('</head>', () => `<script>window.__codemanKeyboardHoldModes=${JSON.stringify(keyboardHoldClis)};</script>\n</head>`);
1648
1714
  if (!soloSessionId && process.env.CODEMAN_GESTURE === '1') {
1649
1715
  html = html.replace('</head>', () => `<script>window.__codemanGestureAvailable=true;</script>\n</head>`);
1650
1716
  if (settings.gestureControlEnabled === true) {
@@ -1714,12 +1780,23 @@ export class WebServer extends EventEmitter {
1714
1780
  const summaryTracker = new RunSummaryTracker(session.id, session.name);
1715
1781
  this.runSummaryTrackers.set(session.id, summaryTracker);
1716
1782
  summaryTracker.recordSessionStarted(session.mode, session.workingDir);
1717
- // Set working directory for Ralph tracker to auto-load @fix_plan.md (not supported for external CLIs)
1783
+ // Set working directory for Ralph tracker to auto-load @fix_plan.md (not supported for external CLIs).
1784
+ // A remote session's workingDir is a path on the remote host, and the watcher reads and
1785
+ // fs.watch()es it on the LOCAL disk, so a remote session only records the path: the fix plan stays
1786
+ // authoritative as for a local session (keeping the output-based completion guessers off), and its
1787
+ // file is read over ssh only on an explicit import (fix-plan/read).
1718
1788
  if (!isExternalCliMode(session.mode)) {
1719
- session.ralphTracker.setWorkingDir(session.workingDir);
1789
+ if (session.remote) {
1790
+ session.ralphTracker.setRemoteWorkingDir(session.workingDir);
1791
+ }
1792
+ else {
1793
+ session.ralphTracker.setWorkingDir(session.workingDir);
1794
+ }
1720
1795
  }
1721
- // Start watching for new images in this session's working directory (if enabled globally and per-session)
1722
- if ((await this.isImageWatcherEnabled()) && session.imageWatcherEnabled) {
1796
+ // Start watching for new images in this session's working directory (if enabled globally and per-session).
1797
+ // A remote session's workingDir is a path on the remote host, and the watcher watches it on the LOCAL
1798
+ // disk, so it is skipped there (the per-session and app-wide toggles skip it too).
1799
+ if ((await this.isImageWatcherEnabled()) && session.imageWatcherEnabled && !session.remote) {
1723
1800
  imageWatcher.watchSession(session.id, session.workingDir);
1724
1801
  }
1725
1802
  // Create and attach all listener handlers via dependency injection
@@ -2917,17 +2994,13 @@ export class WebServer extends EventEmitter {
2917
2994
  // Tunnel only starts when user clicks the toggle in the UI — never on boot.
2918
2995
  // Reset persisted tunnelEnabled so the UI toggle reflects actual state.
2919
2996
  if (await this.isTunnelEnabled()) {
2920
- const settingsPath = dataPath('settings.json');
2921
2997
  try {
2922
- const content = await fs.readFile(settingsPath, 'utf-8');
2923
- const settings = JSON.parse(content);
2924
- settings.tunnelEnabled = false;
2925
- await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
2998
+ await mutateSettingsFile((settings) => ({ ...settings, tunnelEnabled: false }));
2999
+ console.log('Cloudflare tunnel setting reset (tunnel only starts on explicit UI toggle)');
2926
3000
  }
2927
- catch {
2928
- /* ignore */
3001
+ catch (err) {
3002
+ console.warn('Could not reset the Cloudflare tunnel setting:', getErrorMessage(err));
2929
3003
  }
2930
- console.log('Cloudflare tunnel setting reset (tunnel only starts on explicit UI toggle)');
2931
3004
  }
2932
3005
  // Start team watcher for agent team awareness (always on — lightweight polling)
2933
3006
  this.teamWatcher.start();
@@ -3102,6 +3175,9 @@ export class WebServer extends EventEmitter {
3102
3175
  if (saved.autoClearEnabled !== undefined || saved.autoClearThreshold !== undefined) {
3103
3176
  session.setAutoClear(saved.autoClearEnabled ?? false, saved.autoClearThreshold);
3104
3177
  }
3178
+ if (saved.gitFetchEnabled !== undefined || saved.gitFetchIntervalSeconds !== undefined) {
3179
+ session.setGitFetchSettings(saved.gitFetchEnabled, saved.gitFetchIntervalSeconds);
3180
+ }
3105
3181
  if (saved.autoResumeEnabled) {
3106
3182
  // The stamp is re-armed by default, because a Codeman restart leaves the
3107
3183
  // limit footer un-reprinted and dropping it there would strand the pause.
@@ -3318,6 +3394,11 @@ export class WebServer extends EventEmitter {
3318
3394
  // below, a future `expiresAt` re-arms the timer once the pane is attached.
3319
3395
  lastRealPromptAt: savedState?.lastRealPromptAt,
3320
3396
  keepWarmEnabled: savedState?.keepWarmEnabled,
3397
+ // The session's own fetch switch and interval, carried over rather than reset to the app default:
3398
+ // the choice is the user's and outlives the run that made it. The scheduler reads them fresh
3399
+ // on its next tick, so a restored session is back on its own schedule without a restart.
3400
+ gitFetchEnabled: savedState?.gitFetchEnabled,
3401
+ gitFetchIntervalSeconds: savedState?.gitFetchIntervalSeconds,
3321
3402
  // Conversations this pane provably owned, oldest first. Its tail is
3322
3403
  // the conversation the CLI was on when the server stopped, which is
3323
3404
  // what a re-attach must point the viewer at instead of the launch id.
@@ -3749,6 +3830,9 @@ export class WebServer extends EventEmitter {
3749
3830
  sessionWaits.cancelEverything();
3750
3831
  approvalInbox.stop();
3751
3832
  this.keepWarmService.stop();
3833
+ // Stops the fetch sweep from starting another run while the server is closing; a git process already
3834
+ // running is left to finish, which is what a fetch does to a repository nothing else is touching.
3835
+ this.gitFetchScheduler.stop();
3752
3836
  // A post within the debounce window before shutdown would otherwise not reach disk.
3753
3837
  // Flushed again after `app.close()` below, for a post that lands in between.
3754
3838
  agentInboxPersistence.flush();
@@ -3887,8 +3971,8 @@ export class WebServer extends EventEmitter {
3887
3971
  }
3888
3972
  }
3889
3973
  }
3890
- export async function startWebServer(port = 3000, https = false, testMode = false, host = '127.0.0.1', titleHostname, allowUnauthenticatedNetwork = false, basePath = '') {
3891
- const server = new WebServer(port, https, testMode, host, titleHostname, allowUnauthenticatedNetwork, basePath);
3974
+ export async function startWebServer(port = 3000, https = false, testMode = false, host = '127.0.0.1', titleHostname, allowUnauthenticatedNetwork = false, basePath = '', allowUnauthenticatedTunnel = false) {
3975
+ const server = new WebServer(port, https, testMode, host, titleHostname, allowUnauthenticatedNetwork, basePath, allowUnauthenticatedTunnel);
3892
3976
  await server.start();
3893
3977
  return server;
3894
3978
  }