aicodeman 1.42.0 → 1.44.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 (350) hide show
  1. package/README.md +13 -12
  2. package/dist/claude-session-title.d.ts +6 -4
  3. package/dist/claude-session-title.d.ts.map +1 -1
  4. package/dist/claude-session-title.js +6 -4
  5. package/dist/claude-session-title.js.map +1 -1
  6. package/dist/cli-agent.d.ts +3 -3
  7. package/dist/cli-agent.d.ts.map +1 -1
  8. package/dist/cli-agent.js +8 -6
  9. package/dist/cli-agent.js.map +1 -1
  10. package/dist/config/cli-registry/patterns.d.ts +1 -1
  11. package/dist/config/cli-registry/patterns.d.ts.map +1 -1
  12. package/dist/config/cli-registry/patterns.js +3 -0
  13. package/dist/config/cli-registry/patterns.js.map +1 -1
  14. package/dist/config/cli-registry/schema.d.ts +1 -0
  15. package/dist/config/cli-registry/schema.d.ts.map +1 -1
  16. package/dist/config/cli-registry/schema.js +5 -3
  17. package/dist/config/cli-registry/schema.js.map +1 -1
  18. package/dist/config/cli-registry/stock.d.ts.map +1 -1
  19. package/dist/config/cli-registry/stock.js +263 -4
  20. package/dist/config/cli-registry/stock.js.map +1 -1
  21. package/dist/config/cli-registry/types.d.ts +13 -5
  22. package/dist/config/cli-registry/types.d.ts.map +1 -1
  23. package/dist/config/map-limits.d.ts +13 -1
  24. package/dist/config/map-limits.d.ts.map +1 -1
  25. package/dist/config/map-limits.js +28 -1
  26. package/dist/config/map-limits.js.map +1 -1
  27. package/dist/config-backup.d.ts +279 -0
  28. package/dist/config-backup.d.ts.map +1 -0
  29. package/dist/config-backup.js +794 -0
  30. package/dist/config-backup.js.map +1 -0
  31. package/dist/cron/cron-service.d.ts +5 -5
  32. package/dist/cron/cron-service.js +6 -6
  33. package/dist/cron/cron-service.js.map +1 -1
  34. package/dist/custom-model-hosts.d.ts +16 -0
  35. package/dist/custom-model-hosts.d.ts.map +1 -1
  36. package/dist/custom-model-hosts.js.map +1 -1
  37. package/dist/hooks-config.d.ts +24 -0
  38. package/dist/hooks-config.d.ts.map +1 -1
  39. package/dist/hooks-config.js +47 -0
  40. package/dist/hooks-config.js.map +1 -1
  41. package/dist/intent-store.d.ts +2 -0
  42. package/dist/intent-store.d.ts.map +1 -1
  43. package/dist/intent-store.js +9 -0
  44. package/dist/intent-store.js.map +1 -1
  45. package/dist/mcp-sync-targets.d.ts +6 -3
  46. package/dist/mcp-sync-targets.d.ts.map +1 -1
  47. package/dist/mcp-sync-targets.js +6 -3
  48. package/dist/mcp-sync-targets.js.map +1 -1
  49. package/dist/mux-interface.d.ts +28 -7
  50. package/dist/mux-interface.d.ts.map +1 -1
  51. package/dist/prompt-cache-keepwarm.d.ts +193 -0
  52. package/dist/prompt-cache-keepwarm.d.ts.map +1 -0
  53. package/dist/prompt-cache-keepwarm.js +359 -0
  54. package/dist/prompt-cache-keepwarm.js.map +1 -0
  55. package/dist/respawn-controller.d.ts.map +1 -1
  56. package/dist/respawn-controller.js +6 -5
  57. package/dist/respawn-controller.js.map +1 -1
  58. package/dist/session-auto-ops.d.ts.map +1 -1
  59. package/dist/session-auto-ops.js +6 -1
  60. package/dist/session-auto-ops.js.map +1 -1
  61. package/dist/session-cli-registry-bridge.d.ts +15 -1
  62. package/dist/session-cli-registry-bridge.d.ts.map +1 -1
  63. package/dist/session-cli-registry-bridge.js +20 -3
  64. package/dist/session-cli-registry-bridge.js.map +1 -1
  65. package/dist/session-submit-verifier.d.ts +19 -0
  66. package/dist/session-submit-verifier.d.ts.map +1 -1
  67. package/dist/session-submit-verifier.js +88 -0
  68. package/dist/session-submit-verifier.js.map +1 -1
  69. package/dist/session.d.ts +83 -12
  70. package/dist/session.d.ts.map +1 -1
  71. package/dist/session.js +150 -16
  72. package/dist/session.js.map +1 -1
  73. package/dist/state-store.d.ts +10 -0
  74. package/dist/state-store.d.ts.map +1 -1
  75. package/dist/state-store.js +35 -0
  76. package/dist/state-store.js.map +1 -1
  77. package/dist/tab-layout-service.d.ts +33 -0
  78. package/dist/tab-layout-service.d.ts.map +1 -1
  79. package/dist/tab-layout-service.js +75 -3
  80. package/dist/tab-layout-service.js.map +1 -1
  81. package/dist/tmux-manager.d.ts +52 -9
  82. package/dist/tmux-manager.d.ts.map +1 -1
  83. package/dist/tmux-manager.js +148 -30
  84. package/dist/tmux-manager.js.map +1 -1
  85. package/dist/tmux-prefix-table.d.ts +54 -0
  86. package/dist/tmux-prefix-table.d.ts.map +1 -0
  87. package/dist/tmux-prefix-table.js +137 -0
  88. package/dist/tmux-prefix-table.js.map +1 -0
  89. package/dist/tui/tui-app.d.ts +9 -1
  90. package/dist/tui/tui-app.d.ts.map +1 -1
  91. package/dist/tui/tui-app.js +21 -8
  92. package/dist/tui/tui-app.js.map +1 -1
  93. package/dist/tui/tui-client.d.ts +3 -2
  94. package/dist/tui/tui-client.d.ts.map +1 -1
  95. package/dist/tui/tui-client.js +3 -2
  96. package/dist/tui/tui-client.js.map +1 -1
  97. package/dist/types/app-state.d.ts +6 -0
  98. package/dist/types/app-state.d.ts.map +1 -1
  99. package/dist/types/app-state.js.map +1 -1
  100. package/dist/types/session.d.ts +72 -5
  101. package/dist/types/session.d.ts.map +1 -1
  102. package/dist/types/session.js +2 -1
  103. package/dist/types/session.js.map +1 -1
  104. package/dist/usage-telemetry.d.ts +15 -0
  105. package/dist/usage-telemetry.d.ts.map +1 -1
  106. package/dist/usage-telemetry.js +45 -2
  107. package/dist/usage-telemetry.js.map +1 -1
  108. package/dist/utils/cli-installed-probes.d.ts +1 -1
  109. package/dist/utils/cli-installed-probes.d.ts.map +1 -1
  110. package/dist/utils/cli-installed-probes.js +4 -2
  111. package/dist/utils/cli-installed-probes.js.map +1 -1
  112. package/dist/utils/copilot-cli-resolver.d.ts +48 -0
  113. package/dist/utils/copilot-cli-resolver.d.ts.map +1 -0
  114. package/dist/utils/copilot-cli-resolver.js +112 -0
  115. package/dist/utils/copilot-cli-resolver.js.map +1 -0
  116. package/dist/utils/copilot-plan-usage.d.ts +46 -0
  117. package/dist/utils/copilot-plan-usage.d.ts.map +1 -0
  118. package/dist/utils/copilot-plan-usage.js +95 -0
  119. package/dist/utils/copilot-plan-usage.js.map +1 -0
  120. package/dist/utils/index.d.ts +3 -0
  121. package/dist/utils/index.d.ts.map +1 -1
  122. package/dist/utils/index.js +3 -0
  123. package/dist/utils/index.js.map +1 -1
  124. package/dist/utils/single-line.d.ts +22 -0
  125. package/dist/utils/single-line.d.ts.map +1 -0
  126. package/dist/utils/single-line.js +30 -0
  127. package/dist/utils/single-line.js.map +1 -0
  128. package/dist/web/approval-inbox.d.ts +6 -0
  129. package/dist/web/approval-inbox.d.ts.map +1 -1
  130. package/dist/web/approval-inbox.js +2 -2
  131. package/dist/web/approval-inbox.js.map +1 -1
  132. package/dist/web/case-path.d.ts +4 -2
  133. package/dist/web/case-path.d.ts.map +1 -1
  134. package/dist/web/case-path.js +13 -4
  135. package/dist/web/case-path.js.map +1 -1
  136. package/dist/web/plan-usage-latest.d.ts +2 -1
  137. package/dist/web/plan-usage-latest.d.ts.map +1 -1
  138. package/dist/web/plan-usage-latest.js +12 -2
  139. package/dist/web/plan-usage-latest.js.map +1 -1
  140. package/dist/web/ports/index.d.ts +1 -0
  141. package/dist/web/ports/index.d.ts.map +1 -1
  142. package/dist/web/ports/keep-warm-port.d.ts +9 -0
  143. package/dist/web/ports/keep-warm-port.d.ts.map +1 -0
  144. package/dist/web/ports/keep-warm-port.js +6 -0
  145. package/dist/web/ports/keep-warm-port.js.map +1 -0
  146. package/dist/web/ports/session-port.d.ts +3 -1
  147. package/dist/web/ports/session-port.d.ts.map +1 -1
  148. package/dist/web/public/admin-ui.js.gz +0 -0
  149. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  150. package/dist/web/public/app.9314ee60.js +43 -0
  151. package/dist/web/public/app.9314ee60.js.br +0 -0
  152. package/dist/web/public/app.9314ee60.js.gz +0 -0
  153. package/dist/web/public/approvals-ui.js.gz +0 -0
  154. package/dist/web/public/{constants.12b0c247.js → constants.4866ecc3.js} +205 -3
  155. package/dist/web/public/constants.4866ecc3.js.br +0 -0
  156. package/dist/web/public/constants.4866ecc3.js.gz +0 -0
  157. package/dist/web/public/cron-ui.js.gz +0 -0
  158. package/dist/web/public/entrance-animations.js.gz +0 -0
  159. package/dist/web/public/git-status-ui.js.gz +0 -0
  160. package/dist/web/public/home-sessions.js.gz +0 -0
  161. package/dist/web/public/host-wake-ui.js.gz +0 -0
  162. package/dist/web/public/i18n.22ca902b.js +2 -0
  163. package/dist/web/public/i18n.22ca902b.js.br +0 -0
  164. package/dist/web/public/i18n.22ca902b.js.gz +0 -0
  165. package/dist/web/public/{image-input.fcdcb46c.js → image-input.941386e7.js} +24 -14
  166. package/dist/web/public/image-input.941386e7.js.br +0 -0
  167. package/dist/web/public/image-input.941386e7.js.gz +0 -0
  168. package/dist/web/public/index.html +110 -16
  169. package/dist/web/public/index.html.br +0 -0
  170. package/dist/web/public/index.html.gz +0 -0
  171. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  172. package/dist/web/public/{keyboard-accessory.b42f539b.js → keyboard-accessory.4e8b20ac.js} +51 -22
  173. package/dist/web/public/keyboard-accessory.4e8b20ac.js.br +0 -0
  174. package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
  175. package/dist/web/public/{mobile-handlers.0ef9c311.js → mobile-handlers.44adaa07.js} +48 -16
  176. package/dist/web/public/mobile-handlers.44adaa07.js.br +0 -0
  177. package/dist/web/public/mobile-handlers.44adaa07.js.gz +0 -0
  178. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  179. package/dist/web/public/mobile-overview.js +1 -0
  180. package/dist/web/public/mobile-overview.js.br +0 -0
  181. package/dist/web/public/mobile-overview.js.gz +0 -0
  182. package/dist/web/public/mobile.12fb18de.css +1 -0
  183. package/dist/web/public/mobile.12fb18de.css.br +0 -0
  184. package/dist/web/public/mobile.12fb18de.css.gz +0 -0
  185. package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
  186. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  187. package/dist/web/public/{panels-ui.73ec0137.js → panels-ui.cbb24ba1.js} +66 -66
  188. package/dist/web/public/panels-ui.cbb24ba1.js.br +0 -0
  189. package/dist/web/public/panels-ui.cbb24ba1.js.gz +0 -0
  190. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  191. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  192. package/dist/web/public/readmymind-ui.js.gz +0 -0
  193. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  194. package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
  195. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  196. package/dist/web/public/security-notices.json +21 -0
  197. package/dist/web/public/session-lineage.js.gz +0 -0
  198. package/dist/web/public/{session-ui.d52ff6aa.js → session-ui.9c1f009b.js} +8 -4
  199. package/dist/web/public/session-ui.9c1f009b.js.br +0 -0
  200. package/dist/web/public/session-ui.9c1f009b.js.gz +0 -0
  201. package/dist/web/public/settings-ui.fea9ddae.js +106 -0
  202. package/dist/web/public/settings-ui.fea9ddae.js.br +0 -0
  203. package/dist/web/public/settings-ui.fea9ddae.js.gz +0 -0
  204. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  205. package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
  206. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  207. package/dist/web/public/styles.e096fcd6.css +1 -0
  208. package/dist/web/public/styles.e096fcd6.css.br +0 -0
  209. package/dist/web/public/styles.e096fcd6.css.gz +0 -0
  210. package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
  211. package/dist/web/public/sw.js +2 -2
  212. package/dist/web/public/sw.js.br +0 -0
  213. package/dist/web/public/sw.js.gz +0 -0
  214. package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
  215. package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
  216. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  217. package/dist/web/public/terminal-split.js.gz +0 -0
  218. package/dist/web/public/terminal-tile.js +41 -22
  219. package/dist/web/public/terminal-tile.js.br +0 -0
  220. package/dist/web/public/terminal-tile.js.gz +0 -0
  221. package/dist/web/public/terminal-ui.68dbd352.js +2 -0
  222. package/dist/web/public/terminal-ui.68dbd352.js.br +0 -0
  223. package/dist/web/public/terminal-ui.68dbd352.js.gz +0 -0
  224. package/dist/web/public/tile-grid.js +3 -6
  225. package/dist/web/public/tile-grid.js.br +0 -0
  226. package/dist/web/public/tile-grid.js.gz +0 -0
  227. package/dist/web/public/ultracode-panel.js.gz +0 -0
  228. package/dist/web/public/ultracode-windows.js.gz +0 -0
  229. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  230. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  231. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  232. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  233. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  234. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  235. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  236. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  237. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  238. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
  239. package/dist/web/public/vendor/xterm.css.gz +0 -0
  240. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  241. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  242. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  243. package/dist/web/public/webview-tabs.js.gz +0 -0
  244. package/dist/web/public/whats-new.json +9 -9
  245. package/dist/web/response-viewer-transcript.d.ts +6 -0
  246. package/dist/web/response-viewer-transcript.d.ts.map +1 -1
  247. package/dist/web/response-viewer-transcript.js +134 -1
  248. package/dist/web/response-viewer-transcript.js.map +1 -1
  249. package/dist/web/routes/admin-routes.d.ts +2 -1
  250. package/dist/web/routes/admin-routes.d.ts.map +1 -1
  251. package/dist/web/routes/admin-routes.js +3 -0
  252. package/dist/web/routes/admin-routes.js.map +1 -1
  253. package/dist/web/routes/case-routes.d.ts +2 -2
  254. package/dist/web/routes/case-routes.d.ts.map +1 -1
  255. package/dist/web/routes/case-routes.js +81 -3
  256. package/dist/web/routes/case-routes.js.map +1 -1
  257. package/dist/web/routes/config-backup-routes.d.ts +54 -0
  258. package/dist/web/routes/config-backup-routes.d.ts.map +1 -0
  259. package/dist/web/routes/config-backup-routes.js +268 -0
  260. package/dist/web/routes/config-backup-routes.js.map +1 -0
  261. package/dist/web/routes/custom-model-routes.d.ts +17 -1
  262. package/dist/web/routes/custom-model-routes.d.ts.map +1 -1
  263. package/dist/web/routes/custom-model-routes.js +118 -13
  264. package/dist/web/routes/custom-model-routes.js.map +1 -1
  265. package/dist/web/routes/hook-event-routes.d.ts.map +1 -1
  266. package/dist/web/routes/hook-event-routes.js +26 -5
  267. package/dist/web/routes/hook-event-routes.js.map +1 -1
  268. package/dist/web/routes/index.d.ts +1 -0
  269. package/dist/web/routes/index.d.ts.map +1 -1
  270. package/dist/web/routes/index.js +1 -0
  271. package/dist/web/routes/index.js.map +1 -1
  272. package/dist/web/routes/mcp-sync-routes.d.ts +4 -4
  273. package/dist/web/routes/mcp-sync-routes.js +4 -4
  274. package/dist/web/routes/ralph-routes.js +2 -2
  275. package/dist/web/routes/ralph-routes.js.map +1 -1
  276. package/dist/web/routes/reboot-restore-routes.d.ts.map +1 -1
  277. package/dist/web/routes/reboot-restore-routes.js +2 -0
  278. package/dist/web/routes/reboot-restore-routes.js.map +1 -1
  279. package/dist/web/routes/session-routes.d.ts +14 -4
  280. package/dist/web/routes/session-routes.d.ts.map +1 -1
  281. package/dist/web/routes/session-routes.js +131 -30
  282. package/dist/web/routes/session-routes.js.map +1 -1
  283. package/dist/web/routes/status-telemetry-routes.d.ts +2 -2
  284. package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
  285. package/dist/web/routes/status-telemetry-routes.js +8 -4
  286. package/dist/web/routes/status-telemetry-routes.js.map +1 -1
  287. package/dist/web/routes/system-routes.d.ts +2 -2
  288. package/dist/web/routes/system-routes.d.ts.map +1 -1
  289. package/dist/web/routes/system-routes.js +18 -0
  290. package/dist/web/routes/system-routes.js.map +1 -1
  291. package/dist/web/routes/webview-routes.d.ts.map +1 -1
  292. package/dist/web/routes/webview-routes.js +1 -0
  293. package/dist/web/routes/webview-routes.js.map +1 -1
  294. package/dist/web/schemas.d.ts +32 -0
  295. package/dist/web/schemas.d.ts.map +1 -1
  296. package/dist/web/schemas.js +64 -4
  297. package/dist/web/schemas.js.map +1 -1
  298. package/dist/web/server.d.ts +12 -2
  299. package/dist/web/server.d.ts.map +1 -1
  300. package/dist/web/server.js +127 -6
  301. package/dist/web/server.js.map +1 -1
  302. package/dist/web/session-listener-wiring.d.ts +6 -0
  303. package/dist/web/session-listener-wiring.d.ts.map +1 -1
  304. package/dist/web/session-listener-wiring.js +10 -3
  305. package/dist/web/session-listener-wiring.js.map +1 -1
  306. package/dist/web/sse-events.d.ts +5 -2
  307. package/dist/web/sse-events.d.ts.map +1 -1
  308. package/dist/web/sse-events.js +5 -2
  309. package/dist/web/sse-events.js.map +1 -1
  310. package/dist/web/webview-proxy.d.ts +8 -3
  311. package/dist/web/webview-proxy.d.ts.map +1 -1
  312. package/dist/web/webview-proxy.js +23 -5
  313. package/dist/web/webview-proxy.js.map +1 -1
  314. package/package.json +1 -1
  315. package/skills/codeman/SKILL.md +23 -11
  316. package/skills/codeman/preamble.sh +12 -2
  317. package/skills/codeman/reference/endpoints.md +16 -15
  318. package/skills/codeman/reference/messaging.md +5 -5
  319. package/skills/codeman/reference/recipes.md +2 -2
  320. package/skills/codeman/reference/verbs.md +25 -17
  321. package/dist/web/public/app.f04bb2a5.js +0 -43
  322. package/dist/web/public/app.f04bb2a5.js.br +0 -0
  323. package/dist/web/public/app.f04bb2a5.js.gz +0 -0
  324. package/dist/web/public/constants.12b0c247.js.br +0 -0
  325. package/dist/web/public/constants.12b0c247.js.gz +0 -0
  326. package/dist/web/public/i18n.06659676.js +0 -2
  327. package/dist/web/public/i18n.06659676.js.br +0 -0
  328. package/dist/web/public/i18n.06659676.js.gz +0 -0
  329. package/dist/web/public/image-input.fcdcb46c.js.br +0 -0
  330. package/dist/web/public/image-input.fcdcb46c.js.gz +0 -0
  331. package/dist/web/public/keyboard-accessory.b42f539b.js.br +0 -0
  332. package/dist/web/public/keyboard-accessory.b42f539b.js.gz +0 -0
  333. package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
  334. package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
  335. package/dist/web/public/mobile.2dff1580.css +0 -1
  336. package/dist/web/public/mobile.2dff1580.css.br +0 -0
  337. package/dist/web/public/mobile.2dff1580.css.gz +0 -0
  338. package/dist/web/public/panels-ui.73ec0137.js.br +0 -0
  339. package/dist/web/public/panels-ui.73ec0137.js.gz +0 -0
  340. package/dist/web/public/session-ui.d52ff6aa.js.br +0 -0
  341. package/dist/web/public/session-ui.d52ff6aa.js.gz +0 -0
  342. package/dist/web/public/settings-ui.3a22bd2a.js +0 -97
  343. package/dist/web/public/settings-ui.3a22bd2a.js.br +0 -0
  344. package/dist/web/public/settings-ui.3a22bd2a.js.gz +0 -0
  345. package/dist/web/public/styles.ba6a9b84.css +0 -1
  346. package/dist/web/public/styles.ba6a9b84.css.br +0 -0
  347. package/dist/web/public/styles.ba6a9b84.css.gz +0 -0
  348. package/dist/web/public/terminal-ui.0a7ccc29.js +0 -2
  349. package/dist/web/public/terminal-ui.0a7ccc29.js.br +0 -0
  350. package/dist/web/public/terminal-ui.0a7ccc29.js.gz +0 -0
@@ -101,12 +101,8 @@ Object.assign(CodemanApp.prototype, {
101
101
  if (!this.activeSessionId) return;
102
102
  if (!e.dataTransfer || !e.dataTransfer.files.length) return;
103
103
 
104
- const mediaFiles = Array.from(e.dataTransfer.files).filter((f) => this._promptAttachKind(f));
105
- if (mediaFiles.length === 0) {
106
- this.showToast('Unsupported file type', 'error');
107
- return;
108
- }
109
- this._uploadAndInsertImages(mediaFiles);
104
+ const mediaFiles = this._admitPromptAttachments(Array.from(e.dataTransfer.files));
105
+ if (mediaFiles.length > 0) this._uploadAndInsertImages(mediaFiles);
110
106
  });
111
107
  },
112
108
 
@@ -122,11 +118,8 @@ Object.assign(CodemanApp.prototype, {
122
118
  * Without the overlay (local echo off) there is nothing to buffer into, so the
123
119
  * text goes straight out. The trailing space keeps the next word off the path. */
124
120
  async _attachFromToolbar(files, sessionId) {
125
- const media = files.filter((f) => this._promptAttachKind(f));
126
- if (media.length === 0) {
127
- this.showToast('Unsupported file type', 'error');
128
- return;
129
- }
121
+ const media = this._admitPromptAttachments(files);
122
+ if (media.length === 0) return;
130
123
  if (!sessionId) return; // no captured session: refuse rather than guess at one
131
124
  const paths = await this._uploadAndInsertImages(media, { insert: false, sessionId });
132
125
  if (paths.length === 0) return;
@@ -231,14 +224,16 @@ Object.assign(CodemanApp.prototype, {
231
224
  pasteConsumed = true;
232
225
 
233
226
  // Check for images, videos or documents in clipboard items
234
- var imageFiles = [];
227
+ var pastedFiles = [];
235
228
  var items = e.clipboardData && e.clipboardData.items;
236
229
  if (items) {
237
230
  for (var i = 0; i < items.length; i++) {
238
231
  var blob = items[i].getAsFile();
239
- if (blob && self._promptAttachKind(blob)) imageFiles.push(blob);
232
+ if (blob) pastedFiles.push(blob);
240
233
  }
241
234
  }
235
+ var text = e.clipboardData ? e.clipboardData.getData('text/plain') : '';
236
+ var imageFiles = self._admitPromptAttachments(pastedFiles, { textPasted: !!text });
242
237
 
243
238
  // Clean up the trap
244
239
  setTimeout(function() {
@@ -256,7 +251,6 @@ Object.assign(CodemanApp.prototype, {
256
251
  // via raw sendInput() strips those markers and makes pasted input
257
252
  // indistinguishable from typed input, weakening the CLI's
258
253
  // prompt-injection defenses.
259
- var text = e.clipboardData ? e.clipboardData.getData('text/plain') : '';
260
254
  if (text && terminal) terminal.paste(text);
261
255
  }
262
256
  });
@@ -290,6 +284,22 @@ Object.assign(CodemanApp.prototype, {
290
284
  return null;
291
285
  },
292
286
 
287
+ /** The files of a pick, drop or paste that _promptAttachKind admits, telling the
288
+ * user about any it refuses: all of them is "Unsupported file type", some of
289
+ * them a count of what was skipped while the rest still upload. A clipboard
290
+ * paste that also carries text passes `textPasted`: when none of its files is
291
+ * admitted the text is pasted instead, so that case stays quiet. */
292
+ _admitPromptAttachments(files, { textPasted = false } = {}) {
293
+ const admitted = files.filter((f) => this._promptAttachKind(f));
294
+ const skipped = files.length - admitted.length;
295
+ if (skipped > 0 && admitted.length > 0) {
296
+ this.showToast(`${skipped} file${skipped > 1 ? 's' : ''} skipped: unsupported type`, 'info');
297
+ } else if (skipped > 0 && !textPasted) {
298
+ this.showToast('Unsupported file type', 'error');
299
+ }
300
+ return admitted;
301
+ },
302
+
293
303
  // Max images accepted in one batch (paste / drop / mobile picker). Each is
294
304
  // uploaded as its own request, so 20 stays under the server's 30 uploads/min
295
305
  // rate limit while covering "select a bunch of photos at once".
@@ -16,8 +16,8 @@
16
16
  <link rel="manifest" href="manifest.json">
17
17
  <title>Codeman</title>
18
18
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%2360a5fa'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='32' height='32' rx='6' fill='%230a0a0a'/%3E%3Cpath d='M18 4L8 18h6l-2 10 10-14h-6z' fill='url(%23g)'/%3E%3C/svg%3E">
19
- <link rel="stylesheet" href="styles.ba6a9b84.css">
20
- <link rel="stylesheet" href="mobile.2dff1580.css" media="(max-width: 1023px)">
19
+ <link rel="stylesheet" href="styles.e096fcd6.css">
20
+ <link rel="stylesheet" href="mobile.12fb18de.css" media="(max-width: 1023px)">
21
21
  <!-- xterm.css loaded async — terminal won't display until xterm.js runs anyway -->
22
22
  <link rel="preload" href="vendor/xterm.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
23
23
  <noscript><link rel="stylesheet" href="vendor/xterm.css"></noscript>
@@ -27,8 +27,8 @@
27
27
  <!-- Symbols font before the terminal first paints — a late-loading icon font
28
28
  leaves tofu in xterm's glyph atlas until something forces a re-render. -->
29
29
  <link rel="preload" href="fonts/symbols-nerd-font-mono.woff2" as="font" type="font/woff2" crossorigin>
30
- <link rel="preload" href="constants.12b0c247.js" as="script">
31
- <link rel="preload" href="app.f04bb2a5.js" as="script">
30
+ <link rel="preload" href="constants.4866ecc3.js" as="script">
31
+ <link rel="preload" href="app.9314ee60.js" as="script">
32
32
  <!-- Self-hosted xterm.js — eliminates CDN DNS/TLS latency (~100ms).
33
33
  'defer' preserves execution order (xterm loads before fit addon). -->
34
34
  <script defer src="vendor/xterm.min.js"></script>
@@ -197,7 +197,7 @@
197
197
  <button class="btn-icon-header btn-split btn-split--hidden" onclick="app.openSplitPicker(event)" title="Split: open a second session beside this one" aria-label="Split: open a second session beside this one" aria-pressed="false"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="18" rx="2"/><line x1="12" y1="3" x2="12" y2="21"/></svg></button>
198
198
  <button class="btn-icon-header btn-tile-grid btn-tile-grid--hidden" onclick="app.toggleTileGrid()" oncontextmenu="app.openTileCountMenu(event)" aria-describedby="tileGridHint" aria-label="Tiles: show several sessions side by side (right-click for how many)" aria-pressed="false"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="8" height="8" rx="1"/><rect x="13" y="3" width="8" height="8" rx="1"/><rect x="3" y="13" width="8" height="8" rx="1"/><rect x="13" y="13" width="8" height="8" rx="1"/></svg></button>
199
199
  <button class="btn-icon-header btn-ultracode-agents btn-ultracode-agents--hidden" onclick="app.toggleUltracodeAgentsPanel()" title="Ultracode / Workflow agents" aria-label="Open ultracode workflow agents"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="6" cy="6" r="2.5"/><circle cx="6" cy="18" r="2.5"/><circle cx="18" cy="12" r="2.5"/><path d="M8.2 7.2 15.6 11M8.2 16.8 15.6 13"/></svg></button>
200
- <div class="header-plan-usage header-plan-usage--hidden" id="planUsageChip" title="Claude and Codex plan usage limits">—</div>
200
+ <div class="header-plan-usage header-plan-usage--hidden" id="planUsageChip" title="Claude, Codex and Copilot plan usage limits">—</div>
201
201
  <button class="btn-icon-header btn-notifications" onclick="app.toggleNotifications()" title="Notifications" aria-label="Toggle notifications" style="display:none;">
202
202
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
203
203
  <span class="notification-badge" id="notifBadge" style="display:none;">0</span>
@@ -614,6 +614,8 @@
614
614
  <div class="file-preview-header">
615
615
  <span class="file-preview-title" id="filePreviewTitle">file.ts</span>
616
616
  <div class="file-preview-actions">
617
+ <button class="btn-icon-sm" id="filePreviewBackBtn" onclick="app.stepFilePreviewHistory(-1)" title="Back (Alt+←)" aria-label="Back to the previous file" hidden disabled>&larr;</button>
618
+ <button class="btn-icon-sm" id="filePreviewForwardBtn" onclick="app.stepFilePreviewHistory(1)" title="Forward (Alt+→)" aria-label="Forward to the next file" hidden disabled>&rarr;</button>
617
619
  <button class="btn-icon-sm" id="filePreviewPrevBtn" onclick="app.stepFolderPreview(-1)" title="Previous file in folder (←)" aria-label="Previous file in folder" hidden>&lsaquo;</button>
618
620
  <button class="btn-icon-sm" id="filePreviewNextBtn" onclick="app.stepFolderPreview(1)" title="Next file in folder (→)" aria-label="Next file in folder" hidden>&rsaquo;</button>
619
621
  <button class="btn-icon-sm" id="filePreviewFolderBtn" onclick="app.openFolderOfPreview()" title="Show folder" aria-label="Show folder" hidden><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg></button>
@@ -624,6 +626,7 @@
624
626
  <button class="btn-icon-sm file-preview-edit-btn" id="filePreviewEditBtn" onclick="app.enterFilePreviewEdit()" title="Edit file" aria-label="Edit file" hidden><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5z"/></svg></button>
625
627
  <button class="btn-icon-sm" onclick="app.copyFilePreviewContent()" title="Copy content">&#x2398;</button>
626
628
  <button class="btn-icon-sm" id="filePreviewDetachBtn" onclick="app.detachFilePreview()" title="Open in new tab" aria-label="Open in new tab" hidden><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg></button>
629
+ <button class="btn-icon-sm" id="filePreviewExpandBtn" onclick="app.toggleFilePreviewExpanded()" title="Expand" aria-label="Expand" aria-pressed="false"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg></button>
627
630
  <button class="btn-icon-sm" onclick="app.closeFilePreview()" title="Close">&times;</button>
628
631
  </div>
629
632
  </div>
@@ -635,6 +638,7 @@
635
638
  <button class="file-preview-editbar-btn file-preview-editbar-btn--save" id="filePreviewSaveBtn" onclick="app.saveFilePreviewEdit()" disabled>Save</button>
636
639
  </div>
637
640
  <div class="file-preview-footer" id="filePreviewFooter"></div>
641
+ <div class="file-preview-resize" id="filePreviewResize" title="Drag to resize" aria-hidden="true"></div>
638
642
  </div>
639
643
  </div>
640
644
 
@@ -774,6 +778,11 @@
774
778
  <span>1M Opus Context</span>
775
779
  </label>
776
780
  <span class="form-hint">Use 1M token context window for new sessions</span>
781
+ <label class="case-default-group-label" for="caseDefaultGroup">Default session group</label>
782
+ <select id="caseDefaultGroup" class="case-default-group-select" onchange="app.onCaseDefaultGroupChanged(this)">
783
+ <option value="">Ungrouped</option>
784
+ </select>
785
+ <span class="form-hint">New sessions for this case land in this group</span>
777
786
  </div>
778
787
  </div>
779
788
  <!-- Mobile-only case button + gear -->
@@ -818,6 +827,11 @@
818
827
  <span>1M Opus Context</span>
819
828
  </label>
820
829
  <span class="form-hint">Use 1M token context window</span>
830
+ <label class="case-default-group-label" for="caseDefaultGroupMobile">Default session group</label>
831
+ <select id="caseDefaultGroupMobile" class="case-default-group-select" onchange="app.onCaseDefaultGroupChanged(this)">
832
+ <option value="">Ungrouped</option>
833
+ </select>
834
+ <span class="form-hint">New sessions for this case land in this group</span>
821
835
  </div>
822
836
  </div>
823
837
 
@@ -922,6 +936,7 @@
922
936
  <div><kbd>Ctrl</kbd>+<kbd>?</kbd></div><div>Show Shortcuts</div>
923
937
  <div><kbd>Alt/Option</kbd>+<kbd>?</kbd></div><div>Show Shortcuts</div>
924
938
  <div><kbd>Escape</kbd></div><div>Close Panels</div>
939
+ <div><kbd>Alt/Option</kbd>+<kbd>ArrowLeft</kbd> / <kbd>Alt/Option</kbd>+<kbd>ArrowRight</kbd></div><div>File Viewer Back / Forward</div>
925
940
  </div>
926
941
  </section>
927
942
  </div>
@@ -1078,6 +1093,7 @@
1078
1093
  <option value="grok">Grok</option>
1079
1094
  <option value="deepseek">DeepSeek</option>
1080
1095
  <option value="omp">OMP</option>
1096
+ <option value="copilot">GitHub Copilot</option>
1081
1097
  </select>
1082
1098
  </div>
1083
1099
  <div class="form-row"><label>Working Directory</label><input type="text" id="schWorkingDir" placeholder="/absolute/path"></div>
@@ -1333,8 +1349,19 @@
1333
1349
  <div class="set-row-text">
1334
1350
  <span class="set-row-label">Status</span>
1335
1351
  <span class="set-row-desc" id="promptCacheStatus" data-i18n-skip>Not reported yet.</span>
1352
+ <span class="set-row-desc" id="keepWarmStatus" data-i18n-skip hidden></span>
1336
1353
  </div>
1337
1354
  </div>
1355
+ <!-- Per-session opt-in for the keep-alive nudge (prompt-cache-keepwarm.ts);
1356
+ the default for sessions that have not chosen lives in App Settings →
1357
+ Automation. Its status shares the Status row above: session-ui.js updateKeepWarmStatus. -->
1358
+ <div class="set-row">
1359
+ <div class="set-row-text">
1360
+ <span class="set-row-label">Keep the cache warm while idle</span>
1361
+ <span class="set-row-desc">Sends a one-line keep-alive just before the cache expires, so your next prompt still reads from cache. Runs only while idle, up to the limit in App Settings &rarr; System &rarr; Automation; switch on before launch to also stop Claude Code's own idle compaction.</span>
1362
+ </div>
1363
+ <label class="switch switch-sm"><input type="checkbox" id="modalKeepWarmEnabled" onchange="app.autoSaveKeepWarm()"><span class="slider"></span></label>
1364
+ </div>
1338
1365
  </div>
1339
1366
  </div>
1340
1367
 
@@ -2787,6 +2814,13 @@
2787
2814
  </div>
2788
2815
  <label class="switch switch-sm"><input type="checkbox" id="appSettingsWorkspaceHooks"><span class="slider"></span></label>
2789
2816
  </div>
2817
+ <div class="set-row" data-search="pin cli session name resume title tab label">
2818
+ <div class="set-row-text">
2819
+ <span class="set-row-label">Pin tab names on the CLI</span>
2820
+ <span class="set-row-desc">Launch every Claude session with its tab name, so the CLI's prompt label and /resume list match your tabs. Claude then stops generating its own titles for those sessions, and a rename in Codeman holds only until the next turn. Applies from the next session start or respawn, no server restart needed.</span>
2821
+ </div>
2822
+ <label class="switch switch-sm"><input type="checkbox" id="appSettingsPinCliSessionName"><span class="slider"></span></label>
2823
+ </div>
2790
2824
  <div class="set-row" data-search="remote auto reconnect ssh">
2791
2825
  <div class="set-row-text">
2792
2826
  <span class="set-row-label">Remote auto-reconnect</span>
@@ -3234,6 +3268,48 @@
3234
3268
  </div>
3235
3269
  </div>
3236
3270
 
3271
+ <div class="set-group" id="configBackupGroup">
3272
+ <div class="set-group-head"><h4>Config backups</h4><span class="set-scope">server</span></div>
3273
+ <div class="set-group-body">
3274
+ <div class="set-row" data-search="config backup snapshot settings clis restore retention keep">
3275
+ <div class="set-row-text">
3276
+ <span class="set-row-label">Back up my configuration</span>
3277
+ <span class="set-row-desc">Keeps timestamped copies of your settings, CLI overrides, custom endpoints, webhook, linked cases and (in multi-user mode) accounts. A copy is made at startup and whenever one of them changes. Some of these files hold credentials, so each copy is private to this user (mode 0700/0600).</span>
3278
+ </div>
3279
+ <label class="switch switch-sm"><input type="checkbox" id="appSettingsConfigBackup"><span class="slider"></span></label>
3280
+ </div>
3281
+ <div class="set-row has-field" data-search="config backup folder location path directory">
3282
+ <div class="set-row-text">
3283
+ <span class="set-row-label">Backup folder</span>
3284
+ <span class="set-row-desc">An absolute path (a leading <code>~</code> is expanded). Leave empty for the default shown. Only folders Codeman created there are ever pruned.</span>
3285
+ </div>
3286
+ <input type="text" id="appSettingsConfigBackupDir" class="set-input" placeholder="default: ~/.codeman/backups/config" data-i18n-skip>
3287
+ </div>
3288
+ <div class="set-row has-field" data-search="config backup retention keep count snapshots">
3289
+ <div class="set-row-text">
3290
+ <span class="set-row-label">Keep this many backups</span>
3291
+ <span class="set-row-desc">2 to 500 (with 1, every change would delete the state before it). The newest backup is always kept.</span>
3292
+ </div>
3293
+ <input type="number" id="appSettingsConfigBackupKeepCount" oninput="this.dataset.touched=1" class="set-input" min="2" max="500" step="1" placeholder="20">
3294
+ </div>
3295
+ <div class="set-row has-field" data-search="config backup retention keep days age">
3296
+ <div class="set-row-text">
3297
+ <span class="set-row-label">Delete backups older than (days)</span>
3298
+ <span class="set-row-desc">A backup is removed once a newer one has existed this many days. 0 keeps backups until the count above pushes them out.</span>
3299
+ </div>
3300
+ <input type="number" id="appSettingsConfigBackupKeepDays" oninput="this.dataset.touched=1" class="set-input" min="0" max="3650" step="1" placeholder="30">
3301
+ </div>
3302
+ <div class="set-row" data-search="config backup now restore list">
3303
+ <div class="set-row-text">
3304
+ <span class="set-row-label">Backups</span>
3305
+ <span class="set-row-desc">Folder and retention changes apply after you save. Restoring puts a backup's files back and makes a copy of the current ones first. Compare shows what changed between two backups, or between a backup and the running config; credential values are never shown.</span>
3306
+ </div>
3307
+ <button class="btn-toolbar btn-sm btn-primary" id="configBackupNowBtn" onclick="app.backupConfigNow()">Back up now</button>
3308
+ </div>
3309
+ <div id="configBackupList" class="set-note" data-i18n-skip>Loading…</div>
3310
+ </div>
3311
+ </div>
3312
+
3237
3313
  <div class="set-group">
3238
3314
  <div class="set-group-head"><h4>Automation</h4><span class="set-scope">synced</span></div>
3239
3315
  <div class="set-group-body">
@@ -3244,6 +3320,23 @@
3244
3320
  </div>
3245
3321
  <label class="switch switch-sm"><input type="checkbox" id="appSettingsImageWatcherEnabled"><span class="slider"></span></label>
3246
3322
  </div>
3323
+ <div class="set-row" data-search="prompt cache keep warm keep-alive nudge idle expire">
3324
+ <div class="set-row-text">
3325
+ <span class="set-row-label">Keep prompt caches warm</span>
3326
+ <span class="set-row-desc">Default for Claude sessions that have not chosen on their Respawn tab (a session an agent started needs its own switch): shortly before an idle session's prompt cache would expire, send a one-line keep-alive turn so the next prompt still reads the conversation from cache instead of re-processing it.</span>
3327
+ </div>
3328
+ <label class="switch switch-sm"><input type="checkbox" id="appSettingsPromptCacheKeepWarm"><span class="slider"></span></label>
3329
+ </div>
3330
+ <div class="set-row" data-search="prompt cache keep warm hours limit cap window">
3331
+ <div class="set-row-text">
3332
+ <span class="set-row-label">Keep warm for at most</span>
3333
+ <span class="set-row-desc">Hours after your last prompt in a session during which keep-alives are sent; past that the cache is left to expire. A keep-alive costs a small fraction of the re-write it prevents, so a few hours of them pay off, an overnight gap does not.</span>
3334
+ </div>
3335
+ <div class="set-row-actions">
3336
+ <input type="number" id="appSettingsPromptCacheKeepWarmMaxHours" class="set-num" min="0.5" max="24" step="0.5" value="2">
3337
+ <span class="set-unit">hours</span>
3338
+ </div>
3339
+ </div>
3247
3340
  </div>
3248
3341
  </div>
3249
3342
 
@@ -3458,6 +3551,7 @@
3458
3551
  <option value="grok" data-cli="grok">Grok</option>
3459
3552
  <option value="deepseek" data-cli="deepseek">DeepSeek</option>
3460
3553
  <option value="omp" data-cli="omp">OMP</option>
3554
+ <option value="copilot" data-cli="copilot">GitHub Copilot</option>
3461
3555
  <option value="shell">Shell (no agent)</option>
3462
3556
  </select>
3463
3557
  <span class="form-hint">Which CLI to point the Run button at once the clone finishes. Changeable any time from the Run dropdown.</span>
@@ -3636,7 +3730,7 @@
3636
3730
  <div class="form-row docker-create-only">
3637
3731
  <label>Image</label>
3638
3732
  <input type="text" id="dockerImage" placeholder="codeman/agent:base" autocomplete="off" autocapitalize="off" spellcheck="false">
3639
- <span class="form-hint">Build it once with <code>node scripts/build-agent-image.mjs</code>. Contains node + claude/opencode/codex/gemini/agy/pi/grok/dsh/omp + tmux.</span>
3733
+ <span class="form-hint">Build it once with <code>node scripts/build-agent-image.mjs</code>. Contains node + claude/opencode/codex/gemini/agy/pi/grok/dsh/omp/copilot + tmux.</span>
3640
3734
  </div>
3641
3735
  <div class="form-row docker-create-only">
3642
3736
  <label>Network</label>
@@ -4234,12 +4328,12 @@
4234
4328
  <div class="approvals-list"></div>
4235
4329
  </div>
4236
4330
 
4237
- <script defer src="constants.12b0c247.js"></script>
4238
- <script defer src="i18n.06659676.js"></script>
4239
- <script defer src="mobile-handlers.0ef9c311.js"></script>
4331
+ <script defer src="constants.4866ecc3.js"></script>
4332
+ <script defer src="i18n.22ca902b.js"></script>
4333
+ <script defer src="mobile-handlers.44adaa07.js"></script>
4240
4334
  <script defer src="voice-input.2342c37b.js"></script>
4241
4335
  <script defer src="notification-manager.feb82c04.js"></script>
4242
- <script defer src="keyboard-accessory.b42f539b.js"></script>
4336
+ <script defer src="keyboard-accessory.4e8b20ac.js"></script>
4243
4337
  <script defer src="input-cjk.8bc46081.js"></script>
4244
4338
  <!-- Shows iOS Safari IME composition text inside the terminal. Must precede terminal-ui.js. -->
4245
4339
  <script defer src="mobile-ime-preview.2dbaf262.js"></script>
@@ -4249,9 +4343,9 @@
4249
4343
  <script defer src="sanitize-html.e7147e76.js"></script>
4250
4344
  <!-- Owner tab layout projection (grouped vertical rail); pure, read by app.js. -->
4251
4345
  <script defer src="tab-layout-browser.2d6189f7.js"></script>
4252
- <script defer src="app.f04bb2a5.js"></script>
4346
+ <script defer src="app.9314ee60.js"></script>
4253
4347
  <script defer src="tab-rail-resize.7157e0bd.js"></script>
4254
- <script defer src="terminal-ui.0a7ccc29.js"></script>
4348
+ <script defer src="terminal-ui.68dbd352.js"></script>
4255
4349
  <script defer src="terminal-tile.js"></script>
4256
4350
  <script defer src="terminal-split.js"></script>
4257
4351
  <script defer src="tile-grid.js"></script>
@@ -4259,14 +4353,14 @@
4259
4353
  <script defer src="ralph-panel.b350e2ef.js"></script>
4260
4354
  <script defer src="orchestrator-panel.js"></script>
4261
4355
  <script defer src="cron-ui.js"></script>
4262
- <script defer src="settings-ui.3a22bd2a.js"></script>
4263
- <script defer src="panels-ui.73ec0137.js"></script>
4356
+ <script defer src="settings-ui.fea9ddae.js"></script>
4357
+ <script defer src="panels-ui.cbb24ba1.js"></script>
4264
4358
  <script defer src="readmymind-ui.js"></script>
4265
4359
  <script defer src="ultracode-panel.js"></script>
4266
4360
  <script defer src="approvals-ui.js"></script>
4267
4361
  <script defer src="reboot-restore-ui.js"></script>
4268
4362
  <script defer src="admin-ui.js"></script>
4269
- <script defer src="session-ui.d52ff6aa.js"></script>
4363
+ <script defer src="session-ui.9c1f009b.js"></script>
4270
4364
  <script defer src="host-wake-ui.js"></script>
4271
4365
  <script defer src="webview-tabs.js"></script>
4272
4366
  <script defer src="mobile-overview.js"></script>
@@ -4278,7 +4372,7 @@
4278
4372
  <script defer src="subagent-windows.07277a58.js"></script>
4279
4373
  <script defer src="ultracode-windows.js"></script>
4280
4374
  <script defer src="session-lineage.js"></script>
4281
- <script defer src="image-input.fcdcb46c.js"></script>
4375
+ <script defer src="image-input.941386e7.js"></script>
4282
4376
  <script defer src="spreadsheet-preview.75c751de.js"></script>
4283
4377
  </body>
4284
4378
  </html>
Binary file
Binary file
@@ -647,6 +647,9 @@ const COMPOSER_INPUT_FRAME_LIMIT = 64 * 1024;
647
647
  const COMPOSER_PASTE_START = '\x1b[200~';
648
648
  const COMPOSER_PASTE_END = '\x1b[201~';
649
649
 
650
+ // Accessory keys that act on the terminal and leave the keyboard up.
651
+ const ACCESSORY_REFOCUS_ACTIONS = new Set(['scroll-up', 'scroll-down', 'arrow-left', 'arrow-right', 'tab', 'shift-tab', 'shift-left', 'shift-right', 'ctrl', 'ctrl-o', 'opt-enter', 'esc', 'effort-max', 'clear-input']);
652
+
650
653
  /**
651
654
  * KeyboardAccessoryBar - Quick action buttons shown above keyboard when typing.
652
655
  */
@@ -802,6 +805,21 @@ const KeyboardAccessoryBar = {
802
805
  this.syncCodexKeys();
803
806
  this._syncComposerDraftIndicator();
804
807
 
808
+ // A key that keeps the keyboard up must not take focus on press at all.
809
+ // Letting the tap blur the terminal and refocusing it in the click handler
810
+ // (kept below as the fallback) made every such key start a keyboard
811
+ // dismissal: a blur/focus pair per tap. Same pattern as the Attach input
812
+ // (image-input.js). A first tap on clear/compact only arms the confirm.
813
+ this.element.addEventListener('mousedown', (e) => {
814
+ const btn = e.target.closest('.accessory-btn');
815
+ if (!btn) return;
816
+ const action = btn.dataset.action;
817
+ if (ACCESSORY_REFOCUS_ACTIONS.has(action) ||
818
+ ((action === 'clear' || action === 'compact') && this._confirmAction !== action)) {
819
+ e.preventDefault();
820
+ }
821
+ });
822
+
805
823
  // Add click handlers — preventDefault stops event from reaching terminal
806
824
  this.element.addEventListener('click', (e) => {
807
825
  const btn = e.target.closest('.accessory-btn');
@@ -813,8 +831,7 @@ const KeyboardAccessoryBar = {
813
831
  this.handleAction(action, btn);
814
832
 
815
833
  // Refocus terminal so keyboard stays open (tap blurs terminal → keyboard dismisses → toolbar shifts)
816
- const refocusActions = new Set(['scroll-up', 'scroll-down', 'arrow-left', 'arrow-right', 'tab', 'shift-tab', 'shift-left', 'shift-right', 'ctrl', 'ctrl-o', 'opt-enter', 'esc', 'effort-max', 'clear-input']);
817
- if (refocusActions.has(action) ||
834
+ if (ACCESSORY_REFOCUS_ACTIONS.has(action) ||
818
835
  ((action === 'clear' || action === 'compact') && this._confirmAction)) {
819
836
  if (typeof app !== 'undefined' && app.terminal) {
820
837
  app.terminal.focus();
@@ -1339,8 +1356,9 @@ const KeyboardAccessoryBar = {
1339
1356
  this._syncComposerDraftIndicator();
1340
1357
  close({ preserveDraft: false });
1341
1358
  };
1342
- const handleImages = async (files) => {
1343
- const images = Array.from(files || []).filter((file) => app._promptAttachKind(file));
1359
+ // Takes files already admitted by app._admitPromptAttachments (the picker and
1360
+ // the paste below), which also toasts whatever it refused.
1361
+ const handleImages = async (images) => {
1344
1362
  if (images.length === 0 || typeof app._uploadAndInsertImages !== 'function') return;
1345
1363
  saveDraft();
1346
1364
  this._composerUploads.set(sessionId, (this._composerUploads.get(sessionId) || 0) + 1);
@@ -1381,7 +1399,6 @@ const KeyboardAccessoryBar = {
1381
1399
  if (remaining > 0) this._composerUploads.set(sessionId, remaining);
1382
1400
  else this._composerUploads.delete(sessionId);
1383
1401
  syncUploadUi();
1384
- if (overlay.isConnected) fileInput.value = '';
1385
1402
  }
1386
1403
  };
1387
1404
 
@@ -1389,9 +1406,11 @@ const KeyboardAccessoryBar = {
1389
1406
  textarea.addEventListener('paste', (event) => {
1390
1407
  const items = event.clipboardData?.items;
1391
1408
  if (!items) return;
1392
- const images = Array.from(items)
1409
+ const files = Array.from(items)
1393
1410
  .map((item) => item.getAsFile())
1394
- .filter((file) => file && app._promptAttachKind(file));
1411
+ .filter(Boolean);
1412
+ // Pasted text with no admitted file lands in the textarea natively: no toast.
1413
+ const images = app._admitPromptAttachments(files, { textPasted: !!event.clipboardData.getData('text/plain') });
1395
1414
  if (images.length > 0) {
1396
1415
  event.preventDefault();
1397
1416
  void handleImages(images);
@@ -1404,13 +1423,19 @@ const KeyboardAccessoryBar = {
1404
1423
  }
1405
1424
  });
1406
1425
  imageButton.addEventListener('click', () => fileInput.click());
1407
- fileInput.addEventListener('change', () => void handleImages(fileInput.files));
1426
+ fileInput.addEventListener('change', () => {
1427
+ const files = Array.from(fileInput.files || []);
1428
+ // Reset so picking the same refused file again still fires change (and its toast).
1429
+ fileInput.value = '';
1430
+ void handleImages(app._admitPromptAttachments(files));
1431
+ });
1408
1432
  overlay.querySelector('.prompt-composer-terminal').addEventListener('click', () => {
1409
1433
  // Hand the text BACK to the terminal prompt so both surfaces hold the same
1410
1434
  // prompt, the mirror of the adoption composePrompt() does on open. Only a
1411
- // single-line draft can make the trip: the prompt transport is single-line
1412
- // and strips embedded newlines, so a multi-line draft would arrive mangled
1413
- // and instead stays parked as a draft (the dot on the Compose key shows it).
1435
+ // single-line draft can make the trip: the terminal prompt is one line and
1436
+ // its text reaches the PTY as typed input, where an embedded newline is a
1437
+ // key, so a multi-line draft would arrive mangled and instead stays parked
1438
+ // as a draft (the dot on the Compose key shows it).
1414
1439
  const text = textarea.value;
1415
1440
  if (!text || text.includes('\n')) {
1416
1441
  close({ focusTerminal: true });
@@ -1519,10 +1544,11 @@ const KeyboardAccessoryBar = {
1519
1544
  if (this._sendBracketedPaste(sessionId, text, 'Paste')) close();
1520
1545
  };
1521
1546
 
1522
- // Filter to what the server admits, close the dialog, and hand off to the
1523
- // shared upload+insert pipeline. Returns true if any file was handled.
1524
- const handleImages = (files) => {
1525
- const images = Array.from(files || []).filter((f) => app._promptAttachKind(f));
1547
+ // Filter to what the server admits (app._admitPromptAttachments toasts what it
1548
+ // refuses), close the dialog, and hand off to the shared upload+insert
1549
+ // pipeline. Returns true if any file was handled.
1550
+ const handleImages = (files, options) => {
1551
+ const images = app._admitPromptAttachments(Array.from(files || []), options);
1526
1552
  if (images.length === 0) return false;
1527
1553
  close();
1528
1554
  // Upload to, and insert into, the shell this overlay was opened for.
@@ -1532,21 +1558,24 @@ const KeyboardAccessoryBar = {
1532
1558
 
1533
1559
  // File picker (camera / photo library / files) — the reliable mobile path.
1534
1560
  overlay.querySelector('.paste-image').addEventListener('click', () => fileInput.click());
1535
- fileInput.addEventListener('change', () => handleImages(fileInput.files));
1561
+ fileInput.addEventListener('change', () => {
1562
+ const files = Array.from(fileInput.files || []);
1563
+ // Reset so picking the same refused file again still fires change (and its toast).
1564
+ fileInput.value = '';
1565
+ handleImages(files);
1566
+ });
1536
1567
 
1537
1568
  // Best-effort: capture images pasted straight into the textarea.
1538
1569
  textarea.addEventListener('paste', (e) => {
1539
1570
  const items = e.clipboardData && e.clipboardData.items;
1540
1571
  if (!items) return;
1541
- const imageFiles = [];
1572
+ const files = [];
1542
1573
  for (let i = 0; i < items.length; i++) {
1543
1574
  const blob = items[i].getAsFile();
1544
- if (blob && app._promptAttachKind(blob)) imageFiles.push(blob);
1545
- }
1546
- if (imageFiles.length > 0) {
1547
- e.preventDefault();
1548
- handleImages(imageFiles);
1575
+ if (blob) files.push(blob);
1549
1576
  }
1577
+ // Pasted text with no admitted file lands in the textarea natively: no toast.
1578
+ if (handleImages(files, { textPasted: !!e.clipboardData.getData('text/plain') })) e.preventDefault();
1550
1579
  });
1551
1580
 
1552
1581
  overlay.querySelector('.paste-cancel').addEventListener('click', close);