aicodeman 0.2.8 → 0.3.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 (348) hide show
  1. package/README.md +91 -0
  2. package/dist/ai-idle-checker.d.ts.map +1 -1
  3. package/dist/ai-idle-checker.js +3 -2
  4. package/dist/ai-idle-checker.js.map +1 -1
  5. package/dist/ai-plan-checker.d.ts.map +1 -1
  6. package/dist/ai-plan-checker.js +3 -2
  7. package/dist/ai-plan-checker.js.map +1 -1
  8. package/dist/bash-tool-parser.d.ts +2 -3
  9. package/dist/bash-tool-parser.d.ts.map +1 -1
  10. package/dist/bash-tool-parser.js +14 -31
  11. package/dist/bash-tool-parser.js.map +1 -1
  12. package/dist/config/ai-defaults.d.ts +16 -0
  13. package/dist/config/ai-defaults.d.ts.map +1 -0
  14. package/dist/config/ai-defaults.js +16 -0
  15. package/dist/config/ai-defaults.js.map +1 -0
  16. package/dist/config/auth-config.d.ts +19 -0
  17. package/dist/config/auth-config.d.ts.map +1 -0
  18. package/dist/config/auth-config.js +28 -0
  19. package/dist/config/auth-config.js.map +1 -0
  20. package/dist/config/exec-timeout.d.ts +10 -0
  21. package/dist/config/exec-timeout.d.ts.map +1 -0
  22. package/dist/config/exec-timeout.js +10 -0
  23. package/dist/config/exec-timeout.js.map +1 -0
  24. package/dist/config/map-limits.d.ts +4 -0
  25. package/dist/config/map-limits.d.ts.map +1 -1
  26. package/dist/config/map-limits.js +7 -0
  27. package/dist/config/map-limits.js.map +1 -1
  28. package/dist/config/server-timing.d.ts +36 -0
  29. package/dist/config/server-timing.d.ts.map +1 -0
  30. package/dist/config/server-timing.js +51 -0
  31. package/dist/config/server-timing.js.map +1 -0
  32. package/dist/config/team-config.d.ts +16 -0
  33. package/dist/config/team-config.d.ts.map +1 -0
  34. package/dist/config/team-config.js +16 -0
  35. package/dist/config/team-config.js.map +1 -0
  36. package/dist/config/terminal-limits.d.ts +18 -0
  37. package/dist/config/terminal-limits.d.ts.map +1 -0
  38. package/dist/config/terminal-limits.js +18 -0
  39. package/dist/config/terminal-limits.js.map +1 -0
  40. package/dist/config/tunnel-config.d.ts +27 -0
  41. package/dist/config/tunnel-config.d.ts.map +1 -0
  42. package/dist/config/tunnel-config.js +36 -0
  43. package/dist/config/tunnel-config.js.map +1 -0
  44. package/dist/hooks-config.d.ts.map +1 -1
  45. package/dist/hooks-config.js +7 -6
  46. package/dist/hooks-config.js.map +1 -1
  47. package/dist/image-watcher.d.ts +4 -4
  48. package/dist/image-watcher.d.ts.map +1 -1
  49. package/dist/image-watcher.js +17 -30
  50. package/dist/image-watcher.js.map +1 -1
  51. package/dist/index.js +1 -2
  52. package/dist/index.js.map +1 -1
  53. package/dist/plan-orchestrator.d.ts +2 -24
  54. package/dist/plan-orchestrator.d.ts.map +1 -1
  55. package/dist/plan-orchestrator.js.map +1 -1
  56. package/dist/push-store.d.ts +1 -1
  57. package/dist/push-store.d.ts.map +1 -1
  58. package/dist/push-store.js +4 -12
  59. package/dist/push-store.js.map +1 -1
  60. package/dist/ralph-fix-plan-watcher.d.ts +91 -0
  61. package/dist/ralph-fix-plan-watcher.d.ts.map +1 -0
  62. package/dist/ralph-fix-plan-watcher.js +326 -0
  63. package/dist/ralph-fix-plan-watcher.js.map +1 -0
  64. package/dist/ralph-plan-tracker.d.ts +201 -0
  65. package/dist/ralph-plan-tracker.d.ts.map +1 -0
  66. package/dist/ralph-plan-tracker.js +325 -0
  67. package/dist/ralph-plan-tracker.js.map +1 -0
  68. package/dist/ralph-stall-detector.d.ts +84 -0
  69. package/dist/ralph-stall-detector.d.ts.map +1 -0
  70. package/dist/ralph-stall-detector.js +139 -0
  71. package/dist/ralph-stall-detector.js.map +1 -0
  72. package/dist/ralph-status-parser.d.ts +141 -0
  73. package/dist/ralph-status-parser.d.ts.map +1 -0
  74. package/dist/ralph-status-parser.js +478 -0
  75. package/dist/ralph-status-parser.js.map +1 -0
  76. package/dist/ralph-tracker.d.ts +194 -685
  77. package/dist/ralph-tracker.d.ts.map +1 -1
  78. package/dist/ralph-tracker.js +349 -1713
  79. package/dist/ralph-tracker.js.map +1 -1
  80. package/dist/respawn-adaptive-timing.d.ts +61 -0
  81. package/dist/respawn-adaptive-timing.d.ts.map +1 -0
  82. package/dist/respawn-adaptive-timing.js +105 -0
  83. package/dist/respawn-adaptive-timing.js.map +1 -0
  84. package/dist/respawn-controller.d.ts +14 -101
  85. package/dist/respawn-controller.d.ts.map +1 -1
  86. package/dist/respawn-controller.js +155 -594
  87. package/dist/respawn-controller.js.map +1 -1
  88. package/dist/respawn-health.d.ts +54 -0
  89. package/dist/respawn-health.d.ts.map +1 -0
  90. package/dist/respawn-health.js +183 -0
  91. package/dist/respawn-health.js.map +1 -0
  92. package/dist/respawn-metrics.d.ts +81 -0
  93. package/dist/respawn-metrics.d.ts.map +1 -0
  94. package/dist/respawn-metrics.js +198 -0
  95. package/dist/respawn-metrics.js.map +1 -0
  96. package/dist/respawn-patterns.d.ts +45 -0
  97. package/dist/respawn-patterns.d.ts.map +1 -0
  98. package/dist/respawn-patterns.js +125 -0
  99. package/dist/respawn-patterns.js.map +1 -0
  100. package/dist/session-auto-ops.d.ts +89 -0
  101. package/dist/session-auto-ops.d.ts.map +1 -0
  102. package/dist/session-auto-ops.js +224 -0
  103. package/dist/session-auto-ops.js.map +1 -0
  104. package/dist/session-cli-builder.d.ts +62 -0
  105. package/dist/session-cli-builder.d.ts.map +1 -0
  106. package/dist/session-cli-builder.js +121 -0
  107. package/dist/session-cli-builder.js.map +1 -0
  108. package/dist/session-task-cache.d.ts +52 -0
  109. package/dist/session-task-cache.d.ts.map +1 -0
  110. package/dist/session-task-cache.js +90 -0
  111. package/dist/session-task-cache.js.map +1 -0
  112. package/dist/session.d.ts +2 -33
  113. package/dist/session.d.ts.map +1 -1
  114. package/dist/session.js +58 -309
  115. package/dist/session.js.map +1 -1
  116. package/dist/state-store.d.ts +9 -2
  117. package/dist/state-store.d.ts.map +1 -1
  118. package/dist/state-store.js +112 -39
  119. package/dist/state-store.js.map +1 -1
  120. package/dist/subagent-watcher.d.ts +16 -9
  121. package/dist/subagent-watcher.d.ts.map +1 -1
  122. package/dist/subagent-watcher.js +126 -147
  123. package/dist/subagent-watcher.js.map +1 -1
  124. package/dist/team-watcher.d.ts +3 -0
  125. package/dist/team-watcher.d.ts.map +1 -1
  126. package/dist/team-watcher.js +54 -5
  127. package/dist/team-watcher.js.map +1 -1
  128. package/dist/tmux-manager.d.ts.map +1 -1
  129. package/dist/tmux-manager.js +1 -2
  130. package/dist/tmux-manager.js.map +1 -1
  131. package/dist/tunnel-manager.d.ts +26 -0
  132. package/dist/tunnel-manager.d.ts.map +1 -1
  133. package/dist/tunnel-manager.js +127 -7
  134. package/dist/tunnel-manager.js.map +1 -1
  135. package/dist/types/api.d.ts +93 -0
  136. package/dist/types/api.d.ts.map +1 -0
  137. package/dist/types/api.js +83 -0
  138. package/dist/types/api.js.map +1 -0
  139. package/dist/types/app-state.d.ts +100 -0
  140. package/dist/types/app-state.d.ts.map +1 -0
  141. package/dist/types/app-state.js +59 -0
  142. package/dist/types/app-state.js.map +1 -0
  143. package/dist/types/common.d.ts +70 -0
  144. package/dist/types/common.d.ts.map +1 -0
  145. package/dist/types/common.js +8 -0
  146. package/dist/types/common.js.map +1 -0
  147. package/dist/types/index.d.ts +18 -0
  148. package/dist/types/index.d.ts.map +1 -0
  149. package/dist/types/index.js +18 -0
  150. package/dist/types/index.js.map +1 -0
  151. package/dist/types/lifecycle.d.ts +17 -0
  152. package/dist/types/lifecycle.d.ts.map +1 -0
  153. package/dist/types/lifecycle.js +5 -0
  154. package/dist/types/lifecycle.js.map +1 -0
  155. package/dist/types/plan.d.ts +32 -0
  156. package/dist/types/plan.d.ts.map +1 -0
  157. package/dist/types/plan.js +5 -0
  158. package/dist/types/plan.js.map +1 -0
  159. package/dist/types/push.d.ts +23 -0
  160. package/dist/types/push.d.ts.map +1 -0
  161. package/dist/types/push.js +5 -0
  162. package/dist/types/push.js.map +1 -0
  163. package/dist/types/ralph.d.ts +241 -0
  164. package/dist/types/ralph.d.ts.map +1 -0
  165. package/dist/types/ralph.js +49 -0
  166. package/dist/types/ralph.js.map +1 -0
  167. package/dist/types/respawn.d.ts +250 -0
  168. package/dist/types/respawn.d.ts.map +1 -0
  169. package/dist/types/respawn.js +5 -0
  170. package/dist/types/respawn.js.map +1 -0
  171. package/dist/types/run-summary.d.ts +81 -0
  172. package/dist/types/run-summary.d.ts.map +1 -0
  173. package/dist/types/run-summary.js +22 -0
  174. package/dist/types/run-summary.js.map +1 -0
  175. package/dist/types/session.d.ts +130 -0
  176. package/dist/types/session.d.ts.map +1 -0
  177. package/dist/types/session.js +5 -0
  178. package/dist/types/session.js.map +1 -0
  179. package/dist/types/task.d.ts +58 -0
  180. package/dist/types/task.d.ts.map +1 -0
  181. package/dist/types/task.js +5 -0
  182. package/dist/types/task.js.map +1 -0
  183. package/dist/types/teams.d.ts +55 -0
  184. package/dist/types/teams.d.ts.map +1 -0
  185. package/dist/types/teams.js +5 -0
  186. package/dist/types/teams.js.map +1 -0
  187. package/dist/types/tools.d.ts +46 -0
  188. package/dist/types/tools.d.ts.map +1 -0
  189. package/dist/types/tools.js +5 -0
  190. package/dist/types/tools.js.map +1 -0
  191. package/dist/types.d.ts +1 -1138
  192. package/dist/types.d.ts.map +1 -1
  193. package/dist/types.js +1 -214
  194. package/dist/types.js.map +1 -1
  195. package/dist/utils/claude-cli-resolver.d.ts.map +1 -1
  196. package/dist/utils/claude-cli-resolver.js +1 -2
  197. package/dist/utils/claude-cli-resolver.js.map +1 -1
  198. package/dist/utils/debouncer.d.ts +111 -0
  199. package/dist/utils/debouncer.d.ts.map +1 -0
  200. package/dist/utils/debouncer.js +162 -0
  201. package/dist/utils/debouncer.js.map +1 -0
  202. package/dist/utils/index.d.ts +3 -2
  203. package/dist/utils/index.d.ts.map +1 -1
  204. package/dist/utils/index.js +3 -2
  205. package/dist/utils/index.js.map +1 -1
  206. package/dist/utils/opencode-cli-resolver.d.ts.map +1 -1
  207. package/dist/utils/opencode-cli-resolver.js +1 -2
  208. package/dist/utils/opencode-cli-resolver.js.map +1 -1
  209. package/dist/utils/string-similarity.d.ts +0 -57
  210. package/dist/utils/string-similarity.d.ts.map +1 -1
  211. package/dist/utils/string-similarity.js +3 -18
  212. package/dist/utils/string-similarity.js.map +1 -1
  213. package/dist/web/middleware/auth.d.ts +31 -0
  214. package/dist/web/middleware/auth.d.ts.map +1 -0
  215. package/dist/web/middleware/auth.js +154 -0
  216. package/dist/web/middleware/auth.js.map +1 -0
  217. package/dist/web/ports/auth-port.d.ts +18 -0
  218. package/dist/web/ports/auth-port.d.ts.map +1 -0
  219. package/dist/web/ports/auth-port.js +6 -0
  220. package/dist/web/ports/auth-port.js.map +1 -0
  221. package/dist/web/ports/config-port.d.ts +28 -0
  222. package/dist/web/ports/config-port.d.ts.map +1 -0
  223. package/dist/web/ports/config-port.js +6 -0
  224. package/dist/web/ports/config-port.js.map +1 -0
  225. package/dist/web/ports/event-port.d.ts +13 -0
  226. package/dist/web/ports/event-port.d.ts.map +1 -0
  227. package/dist/web/ports/event-port.js +6 -0
  228. package/dist/web/ports/event-port.js.map +1 -0
  229. package/dist/web/ports/index.d.ts +14 -0
  230. package/dist/web/ports/index.d.ts.map +1 -0
  231. package/dist/web/ports/index.js +9 -0
  232. package/dist/web/ports/index.js.map +1 -0
  233. package/dist/web/ports/infra-port.d.ts +36 -0
  234. package/dist/web/ports/infra-port.d.ts.map +1 -0
  235. package/dist/web/ports/infra-port.js +6 -0
  236. package/dist/web/ports/infra-port.js.map +1 -0
  237. package/dist/web/ports/respawn-port.d.ts +20 -0
  238. package/dist/web/ports/respawn-port.d.ts.map +1 -0
  239. package/dist/web/ports/respawn-port.js +6 -0
  240. package/dist/web/ports/respawn-port.js.map +1 -0
  241. package/dist/web/ports/session-port.d.ts +15 -0
  242. package/dist/web/ports/session-port.d.ts.map +1 -0
  243. package/dist/web/ports/session-port.js +6 -0
  244. package/dist/web/ports/session-port.js.map +1 -0
  245. package/dist/web/public/api-client.js +70 -0
  246. package/dist/web/public/api-client.js.br +0 -0
  247. package/dist/web/public/api-client.js.gz +0 -0
  248. package/dist/web/public/app.js +152 -236
  249. package/dist/web/public/app.js.br +0 -0
  250. package/dist/web/public/app.js.gz +0 -0
  251. package/dist/web/public/constants.js +238 -0
  252. package/dist/web/public/constants.js.br +0 -0
  253. package/dist/web/public/constants.js.gz +0 -0
  254. package/dist/web/public/index.html +11 -3
  255. package/dist/web/public/index.html.br +0 -0
  256. package/dist/web/public/index.html.gz +0 -0
  257. package/dist/web/public/keyboard-accessory.js +279 -0
  258. package/dist/web/public/keyboard-accessory.js.br +0 -0
  259. package/dist/web/public/keyboard-accessory.js.gz +0 -0
  260. package/dist/web/public/mobile-handlers.js +467 -0
  261. package/dist/web/public/mobile-handlers.js.br +0 -0
  262. package/dist/web/public/mobile-handlers.js.gz +0 -0
  263. package/dist/web/public/mobile.css.gz +0 -0
  264. package/dist/web/public/notification-manager.js +445 -0
  265. package/dist/web/public/notification-manager.js.br +0 -0
  266. package/dist/web/public/notification-manager.js.gz +0 -0
  267. package/dist/web/public/ralph-wizard.js +3 -3
  268. package/dist/web/public/ralph-wizard.js.br +0 -0
  269. package/dist/web/public/ralph-wizard.js.gz +0 -0
  270. package/dist/web/public/styles.css.gz +0 -0
  271. package/dist/web/public/subagent-windows.js +1115 -0
  272. package/dist/web/public/subagent-windows.js.br +0 -0
  273. package/dist/web/public/subagent-windows.js.gz +0 -0
  274. package/dist/web/public/sw.js.gz +0 -0
  275. package/dist/web/public/upload.html.gz +0 -0
  276. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  277. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  278. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  279. package/dist/web/public/vendor/xterm.css.gz +0 -0
  280. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  281. package/dist/web/public/voice-input.js +858 -0
  282. package/dist/web/public/voice-input.js.br +0 -0
  283. package/dist/web/public/voice-input.js.gz +0 -0
  284. package/dist/web/route-helpers.d.ts +38 -0
  285. package/dist/web/route-helpers.d.ts.map +1 -0
  286. package/dist/web/route-helpers.js +143 -0
  287. package/dist/web/route-helpers.js.map +1 -0
  288. package/dist/web/routes/case-routes.d.ts +9 -0
  289. package/dist/web/routes/case-routes.d.ts.map +1 -0
  290. package/dist/web/routes/case-routes.js +419 -0
  291. package/dist/web/routes/case-routes.js.map +1 -0
  292. package/dist/web/routes/file-routes.d.ts +8 -0
  293. package/dist/web/routes/file-routes.d.ts.map +1 -0
  294. package/dist/web/routes/file-routes.js +337 -0
  295. package/dist/web/routes/file-routes.js.map +1 -0
  296. package/dist/web/routes/hook-event-routes.d.ts +9 -0
  297. package/dist/web/routes/hook-event-routes.d.ts.map +1 -0
  298. package/dist/web/routes/hook-event-routes.js +57 -0
  299. package/dist/web/routes/hook-event-routes.js.map +1 -0
  300. package/dist/web/routes/index.d.ts +16 -0
  301. package/dist/web/routes/index.d.ts.map +1 -0
  302. package/dist/web/routes/index.js +16 -0
  303. package/dist/web/routes/index.js.map +1 -0
  304. package/dist/web/routes/mux-routes.d.ts +8 -0
  305. package/dist/web/routes/mux-routes.d.ts.map +1 -0
  306. package/dist/web/routes/mux-routes.js +32 -0
  307. package/dist/web/routes/mux-routes.js.map +1 -0
  308. package/dist/web/routes/plan-routes.d.ts +9 -0
  309. package/dist/web/routes/plan-routes.d.ts.map +1 -0
  310. package/dist/web/routes/plan-routes.js +381 -0
  311. package/dist/web/routes/plan-routes.js.map +1 -0
  312. package/dist/web/routes/push-routes.d.ts +8 -0
  313. package/dist/web/routes/push-routes.d.ts.map +1 -0
  314. package/dist/web/routes/push-routes.js +49 -0
  315. package/dist/web/routes/push-routes.js.map +1 -0
  316. package/dist/web/routes/ralph-routes.d.ts +9 -0
  317. package/dist/web/routes/ralph-routes.d.ts.map +1 -0
  318. package/dist/web/routes/ralph-routes.js +475 -0
  319. package/dist/web/routes/ralph-routes.js.map +1 -0
  320. package/dist/web/routes/respawn-routes.d.ts +8 -0
  321. package/dist/web/routes/respawn-routes.d.ts.map +1 -0
  322. package/dist/web/routes/respawn-routes.js +260 -0
  323. package/dist/web/routes/respawn-routes.js.map +1 -0
  324. package/dist/web/routes/scheduled-routes.d.ts +8 -0
  325. package/dist/web/routes/scheduled-routes.d.ts.map +1 -0
  326. package/dist/web/routes/scheduled-routes.js +51 -0
  327. package/dist/web/routes/scheduled-routes.js.map +1 -0
  328. package/dist/web/routes/session-routes.d.ts +9 -0
  329. package/dist/web/routes/session-routes.d.ts.map +1 -0
  330. package/dist/web/routes/session-routes.js +729 -0
  331. package/dist/web/routes/session-routes.js.map +1 -0
  332. package/dist/web/routes/system-routes.d.ts +9 -0
  333. package/dist/web/routes/system-routes.d.ts.map +1 -0
  334. package/dist/web/routes/system-routes.js +678 -0
  335. package/dist/web/routes/system-routes.js.map +1 -0
  336. package/dist/web/routes/team-routes.d.ts +8 -0
  337. package/dist/web/routes/team-routes.d.ts.map +1 -0
  338. package/dist/web/routes/team-routes.js +14 -0
  339. package/dist/web/routes/team-routes.js.map +1 -0
  340. package/dist/web/schemas.d.ts +43 -3
  341. package/dist/web/schemas.d.ts.map +1 -1
  342. package/dist/web/schemas.js +6 -2
  343. package/dist/web/schemas.js.map +1 -1
  344. package/dist/web/server.d.ts +10 -9
  345. package/dist/web/server.d.ts.map +1 -1
  346. package/dist/web/server.js +342 -3829
  347. package/dist/web/server.js.map +1 -1
  348. package/package.json +1 -1
Binary file
Binary file
@@ -0,0 +1,238 @@
1
+ // Codeman — Shared constants and utility functions for frontend modules
2
+
3
+ // ============================================================================
4
+ // Web Push Utilities
5
+ // ============================================================================
6
+
7
+ /** Convert a base64-encoded VAPID key to Uint8Array for pushManager.subscribe() */
8
+ function urlBase64ToUint8Array(base64String) {
9
+ const padding = '='.repeat((4 - base64String.length % 4) % 4);
10
+ const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
11
+ const rawData = atob(base64);
12
+ const outputArray = new Uint8Array(rawData.length);
13
+ for (let i = 0; i < rawData.length; ++i) {
14
+ outputArray[i] = rawData.charCodeAt(i);
15
+ }
16
+ return outputArray;
17
+ }
18
+
19
+ // ============================================================================
20
+ // Constants
21
+ // ============================================================================
22
+
23
+ // Default terminal scrollback (can be changed via settings)
24
+ const DEFAULT_SCROLLBACK = 5000;
25
+
26
+ // Timing constants
27
+ const STUCK_THRESHOLD_DEFAULT_MS = 600000; // 10 minutes - default for stuck detection
28
+ const GROUPING_TIMEOUT_MS = 5000; // 5 seconds - notification grouping window
29
+ const NOTIFICATION_LIST_CAP = 100; // Max notifications in list
30
+ const TITLE_FLASH_INTERVAL_MS = 1500; // Title flash rate
31
+ const BROWSER_NOTIF_RATE_LIMIT_MS = 3000; // Rate limit for browser notifications
32
+ const AUTO_CLOSE_NOTIFICATION_MS = 8000; // Auto-close browser notifications
33
+ const THROTTLE_DELAY_MS = 100; // General UI throttle delay
34
+ const TERMINAL_CHUNK_SIZE = 128 * 1024; // 128KB chunks for terminal data
35
+ const TERMINAL_TAIL_SIZE = 256 * 1024; // 256KB tail for initial load
36
+ const SYNC_WAIT_TIMEOUT_MS = 50; // Wait timeout for terminal sync
37
+ const STATS_POLLING_INTERVAL_MS = 2000; // System stats polling
38
+
39
+ // Z-index base values for layered floating windows
40
+ const ZINDEX_SUBAGENT_BASE = 1000;
41
+ const ZINDEX_PLAN_SUBAGENT_BASE = 1100;
42
+ const ZINDEX_LOG_VIEWER_BASE = 2000;
43
+ const ZINDEX_IMAGE_POPUP_BASE = 3000;
44
+
45
+ // Subagent/floating window layout
46
+ const WINDOW_INITIAL_TOP_PX = 120;
47
+ const WINDOW_CASCADE_OFFSET_PX = 30;
48
+ const WINDOW_MIN_WIDTH_PX = 200;
49
+ const WINDOW_MIN_HEIGHT_PX = 200;
50
+ const WINDOW_DEFAULT_WIDTH_PX = 300;
51
+
52
+ // Scheduler API — prioritize terminal writes over background UI updates.
53
+ // scheduler.postTask('background') defers non-critical work (connection lines, panel renders)
54
+ // so the main thread stays free for terminal rendering at 60fps.
55
+ const _hasScheduler = typeof globalThis.scheduler?.postTask === 'function';
56
+ function scheduleBackground(fn) {
57
+ if (_hasScheduler) { scheduler.postTask(fn, { priority: 'background' }); }
58
+ else { requestAnimationFrame(fn); }
59
+ }
60
+
61
+ // DEC mode 2026 - Synchronized Output
62
+ // Wrap terminal writes with these markers to prevent partial-frame flicker.
63
+ // Terminal buffers all output between markers and renders atomically.
64
+ // Supported by: WezTerm, Kitty, Ghostty, iTerm2 3.5+, Windows Terminal, VSCode terminal
65
+ // xterm.js doesn't support DEC 2026 natively, so we implement buffering ourselves.
66
+ const DEC_SYNC_START = '\x1b[?2026h';
67
+ const DEC_SYNC_END = '\x1b[?2026l';
68
+ // Pre-compiled regex for stripping DEC 2026 markers (single pass instead of two replaceAll calls)
69
+ const DEC_SYNC_STRIP_RE = /\x1b\[\?2026[hl]/g;
70
+
71
+ // Built-in respawn configuration presets
72
+ const BUILTIN_RESPAWN_PRESETS = [
73
+ {
74
+ id: 'solo-work',
75
+ name: 'Solo',
76
+ description: 'Claude working alone — fast respawn cycles with context reset',
77
+ config: {
78
+ idleTimeoutMs: 3000,
79
+ updatePrompt: 'summarize your progress so far before the context reset.',
80
+ interStepDelayMs: 2000,
81
+ sendClear: true,
82
+ sendInit: true,
83
+ kickstartPrompt: 'continue working. Pick up where you left off based on the context above.',
84
+ autoAcceptPrompts: true,
85
+ },
86
+ durationMinutes: 60,
87
+ builtIn: true,
88
+ createdAt: 0,
89
+ },
90
+ {
91
+ id: 'subagent-workflow',
92
+ name: 'Subagents',
93
+ description: 'Lead session with Task tool subagents — longer idle tolerance',
94
+ config: {
95
+ idleTimeoutMs: 45000,
96
+ updatePrompt: 'check on your running subagents and summarize their results before the context reset. If all subagents have finished, note what was completed and what remains.',
97
+ interStepDelayMs: 3000,
98
+ sendClear: true,
99
+ sendInit: true,
100
+ kickstartPrompt: 'check on your running subagents and continue coordinating their work. If all subagents have finished, summarize their results and proceed with the next step.',
101
+ autoAcceptPrompts: true,
102
+ },
103
+ durationMinutes: 240,
104
+ builtIn: true,
105
+ createdAt: 0,
106
+ },
107
+ {
108
+ id: 'team-lead',
109
+ name: 'Team',
110
+ description: 'Leading an agent team via TeamCreate — tolerates long silences',
111
+ config: {
112
+ idleTimeoutMs: 90000,
113
+ updatePrompt: 'review the task list and teammate progress. Summarize the current state before the context reset.',
114
+ interStepDelayMs: 5000,
115
+ sendClear: true,
116
+ sendInit: true,
117
+ kickstartPrompt: 'check on your teammates by reviewing the task list and any messages in your inbox. Assign new tasks if teammates are idle, or continue coordinating the team effort.',
118
+ autoAcceptPrompts: true,
119
+ },
120
+ durationMinutes: 480,
121
+ builtIn: true,
122
+ createdAt: 0,
123
+ },
124
+ {
125
+ id: 'ralph-todo',
126
+ name: 'Ralph/Todo',
127
+ description: 'Ralph Loop task list — works through todos with progress tracking',
128
+ config: {
129
+ idleTimeoutMs: 8000,
130
+ updatePrompt: 'update CLAUDE.md with discoveries and progress notes, mark completed tasks in @fix_plan.md, write a brief summary so the next cycle can continue seamlessly.',
131
+ interStepDelayMs: 3000,
132
+ sendClear: true,
133
+ sendInit: true,
134
+ kickstartPrompt: 'read @fix_plan.md for task status, continue on the next uncompleted task. When ALL tasks are complete, output <promise>COMPLETE</promise>.',
135
+ autoAcceptPrompts: true,
136
+ },
137
+ durationMinutes: 480,
138
+ builtIn: true,
139
+ createdAt: 0,
140
+ },
141
+ {
142
+ id: 'overnight-autonomous',
143
+ name: 'Overnight',
144
+ description: 'Unattended overnight runs with full context reset between cycles',
145
+ config: {
146
+ idleTimeoutMs: 10000,
147
+ updatePrompt: 'summarize what you accomplished so far and write key progress notes to CLAUDE.md so the next cycle can pick up where you left off.',
148
+ interStepDelayMs: 3000,
149
+ sendClear: true,
150
+ sendInit: true,
151
+ kickstartPrompt: 'continue working on the task. Pick up where you left off based on the context above.',
152
+ autoAcceptPrompts: true,
153
+ },
154
+ durationMinutes: 480,
155
+ builtIn: true,
156
+ createdAt: 0,
157
+ },
158
+ ];
159
+
160
+ // ============================================================================
161
+ // Utility Functions
162
+ // ============================================================================
163
+
164
+ /**
165
+ * Get unified coordinates from mouse or touch event.
166
+ * @param {MouseEvent|TouchEvent} e - The event
167
+ * @returns {{ clientX: number, clientY: number }} Coordinates
168
+ */
169
+ function getEventCoords(e) {
170
+ if (e.touches && e.touches.length > 0) {
171
+ return { clientX: e.touches[0].clientX, clientY: e.touches[0].clientY };
172
+ }
173
+ if (e.changedTouches && e.changedTouches.length > 0) {
174
+ return { clientX: e.changedTouches[0].clientX, clientY: e.changedTouches[0].clientY };
175
+ }
176
+ return { clientX: e.clientX, clientY: e.clientY };
177
+ }
178
+
179
+ /**
180
+ * Process data containing DEC 2026 sync markers.
181
+ * Strips markers and returns segments that should be written atomically.
182
+ * Each returned segment represents content between SYNC_START and SYNC_END.
183
+ * Content outside sync blocks is returned as-is.
184
+ *
185
+ * @param {string} data - Raw terminal data with potential sync markers
186
+ * @returns {string[]} - Array of content segments to write (markers stripped)
187
+ */
188
+ function extractSyncSegments(data) {
189
+ const segments = [];
190
+ let remaining = data;
191
+
192
+ while (remaining.length > 0) {
193
+ const startIdx = remaining.indexOf(DEC_SYNC_START);
194
+
195
+ if (startIdx === -1) {
196
+ // No more sync blocks, return rest as-is
197
+ if (remaining.length > 0) {
198
+ segments.push(remaining);
199
+ }
200
+ break;
201
+ }
202
+
203
+ // Content before sync block (if any)
204
+ if (startIdx > 0) {
205
+ segments.push(remaining.slice(0, startIdx));
206
+ }
207
+
208
+ // Find matching end marker
209
+ const afterStart = remaining.slice(startIdx + DEC_SYNC_START.length);
210
+ const endIdx = afterStart.indexOf(DEC_SYNC_END);
211
+
212
+ if (endIdx === -1) {
213
+ // No end marker found - sync block continues in next chunk
214
+ // Include the start marker so it can be handled when more data arrives
215
+ segments.push(remaining.slice(startIdx));
216
+ break;
217
+ }
218
+
219
+ // Extract synchronized content (without markers)
220
+ const syncContent = afterStart.slice(0, endIdx);
221
+ if (syncContent.length > 0) {
222
+ segments.push(syncContent);
223
+ }
224
+
225
+ // Continue with content after end marker
226
+ remaining = afterStart.slice(endIdx + DEC_SYNC_END.length);
227
+ }
228
+
229
+ return segments;
230
+ }
231
+
232
+ // HTML escape utility (shared by NotificationManager, CodemanApp, and ralph-wizard.js)
233
+ const _htmlEscapeMap = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
234
+ const _htmlEscapePattern = /[&<>"']/g;
235
+ function escapeHtml(text) {
236
+ if (typeof text !== 'string') return '';
237
+ return text.replace(_htmlEscapePattern, (ch) => _htmlEscapeMap[ch]);
238
+ }
Binary file
Binary file
@@ -20,6 +20,7 @@
20
20
  <script defer src="vendor/xterm-addon-fit.min.js"></script>
21
21
  <script defer src="vendor/xterm-addon-webgl.min.js"></script>
22
22
  <script defer src="vendor/xterm-addon-unicode11.min.js"></script>
23
+ <script defer src="vendor/xterm-zerolag-input.js?v=0.2.9"></script>
23
24
  <!-- Synchronous mobile detection — runs before first paint to prevent panel flash -->
24
25
  <script>if(window.innerWidth<768||(('ontouchstart' in window||navigator.maxTouchPoints>0)&&window.innerWidth<1024))document.documentElement.classList.add('mobile-init');</script>
25
26
  <!-- Inline critical CSS for instant skeleton paint (before styles.css loads) -->
@@ -435,7 +436,7 @@
435
436
  <div class="monitor-panel-header" id="monitorPanelHeader">
436
437
  <div class="monitor-panel-title">Monitor</div>
437
438
  <div class="monitor-panel-actions">
438
- <button class="btn-toolbar btn-sm btn-danger" onclick="app.killAllSessions()" title="Kill all sessions and their tmux processes">Kill All</button>
439
+ <button class="btn-toolbar btn-sm btn-danger" onclick="app.killAllMuxSessions()" title="Kill all sessions and their tmux processes">Kill All</button>
439
440
  <button class="btn-icon-sm" onclick="app.reconcileMuxSessions()" title="Refresh tmux sessions">&#x21BB;</button>
440
441
  <button class="btn-icon-sm" onclick="app.toggleMonitorDetach()" title="Detach panel" id="monitorDetachBtn">&#x29C9;</button>
441
442
  <button class="btn-icon-sm" onclick="app.toggleMonitorPanel()" title="Toggle panel" id="monitorToggleBtn">&#x25B2;</button>
@@ -1669,7 +1670,14 @@
1669
1670
  <!-- Lines drawn dynamically -->
1670
1671
  </svg>
1671
1672
 
1672
- <script defer src="app.js?v=0.1631"></script>
1673
- <script defer src="ralph-wizard.js?v=0.1631"></script>
1673
+ <script defer src="constants.js?v=0.2.9"></script>
1674
+ <script defer src="mobile-handlers.js?v=0.2.9"></script>
1675
+ <script defer src="voice-input.js?v=0.2.9"></script>
1676
+ <script defer src="notification-manager.js?v=0.2.9"></script>
1677
+ <script defer src="keyboard-accessory.js?v=0.2.9"></script>
1678
+ <script defer src="app.js?v=0.2.9"></script>
1679
+ <script defer src="ralph-wizard.js?v=0.2.9"></script>
1680
+ <script defer src="api-client.js?v=0.2.9"></script>
1681
+ <script defer src="subagent-windows.js?v=0.2.9"></script>
1674
1682
  </body>
1675
1683
  </html>
Binary file
Binary file
@@ -0,0 +1,279 @@
1
+ // Codeman — Keyboard accessory bar and focus trap for modals
2
+ // Loaded after mobile-handlers.js, before app.js
3
+
4
+ // ============================================================================
5
+ // Mobile Keyboard Accessory Bar
6
+ // ============================================================================
7
+
8
+ /**
9
+ * KeyboardAccessoryBar - Quick action buttons shown above keyboard when typing.
10
+ */
11
+ const KeyboardAccessoryBar = {
12
+ element: null,
13
+
14
+ /** Create and inject the accessory bar */
15
+ init() {
16
+ // Only on mobile
17
+ if (!MobileDetection.isTouchDevice()) return;
18
+
19
+ // Create accessory bar element
20
+ this.element = document.createElement('div');
21
+ this.element.className = 'keyboard-accessory-bar';
22
+ this.element.innerHTML = `
23
+ <button class="accessory-btn accessory-btn-arrow" data-action="scroll-up" title="Arrow up">
24
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
25
+ <path d="M5 15l7-7 7 7"/>
26
+ </svg>
27
+ </button>
28
+ <button class="accessory-btn accessory-btn-arrow" data-action="scroll-down" title="Arrow down">
29
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
30
+ <path d="M19 9l-7 7-7-7"/>
31
+ </svg>
32
+ </button>
33
+ <button class="accessory-btn" data-action="init" title="/init">/init</button>
34
+ <button class="accessory-btn" data-action="clear" title="/clear">/clear</button>
35
+ <button class="accessory-btn" data-action="compact" title="/compact">/compact</button>
36
+ <button class="accessory-btn" data-action="paste" title="Paste from clipboard">
37
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
38
+ <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
39
+ <rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
40
+ </svg>
41
+ </button>
42
+ <button class="accessory-btn accessory-btn-dismiss" data-action="dismiss" title="Dismiss keyboard">
43
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
44
+ <path d="M19 9l-7 7-7-7"/>
45
+ </svg>
46
+ </button>
47
+ `;
48
+
49
+ // Add click handlers — preventDefault stops event from reaching terminal
50
+ this.element.addEventListener('click', (e) => {
51
+ const btn = e.target.closest('.accessory-btn');
52
+ if (!btn) return;
53
+ e.preventDefault();
54
+ e.stopPropagation();
55
+
56
+ const action = btn.dataset.action;
57
+ this.handleAction(action, btn);
58
+
59
+ // Refocus terminal so keyboard stays open (tap blurs terminal → keyboard dismisses → toolbar shifts)
60
+ if ((action === 'scroll-up' || action === 'scroll-down') ||
61
+ ((action === 'clear' || action === 'compact') && this._confirmAction)) {
62
+ if (typeof app !== 'undefined' && app.terminal) {
63
+ app.terminal.focus();
64
+ }
65
+ }
66
+ });
67
+
68
+ // Insert before toolbar
69
+ const toolbar = document.querySelector('.toolbar');
70
+ if (toolbar && toolbar.parentNode) {
71
+ toolbar.parentNode.insertBefore(this.element, toolbar);
72
+ }
73
+ },
74
+
75
+ _confirmTimer: null,
76
+ _confirmAction: null,
77
+
78
+ /** Handle accessory button actions */
79
+ handleAction(action, btn) {
80
+ if (typeof app === 'undefined' || !app.activeSessionId) return;
81
+
82
+ switch (action) {
83
+ case 'scroll-up':
84
+ this.sendKey('\x1b[A');
85
+ break;
86
+ case 'scroll-down':
87
+ this.sendKey('\x1b[B');
88
+ break;
89
+ case 'init':
90
+ this.sendCommand('/init');
91
+ break;
92
+ case 'clear':
93
+ case 'compact': {
94
+ // Require double-tap: first tap turns amber, second tap within 2s sends
95
+ const cmd = action === 'clear' ? '/clear' : '/compact';
96
+ if (this._confirmAction === action && this._confirmTimer) {
97
+ this.clearConfirm();
98
+ this.sendCommand(cmd);
99
+ } else {
100
+ this.setConfirm(action, btn);
101
+ }
102
+ break;
103
+ }
104
+ case 'paste':
105
+ this.pasteFromClipboard();
106
+ break;
107
+ case 'dismiss':
108
+ // Blur active element to dismiss keyboard
109
+ document.activeElement?.blur();
110
+ break;
111
+ }
112
+ },
113
+
114
+ /** Enter confirm state: button turns amber for 2s waiting for second tap */
115
+ setConfirm(action, btn) {
116
+ this.clearConfirm();
117
+ this._confirmAction = action;
118
+ if (btn) {
119
+ btn.classList.add('confirming');
120
+ btn.dataset.origHtml = btn.innerHTML;
121
+ btn.textContent = 'Tap again';
122
+ }
123
+ this._confirmTimer = setTimeout(() => this.clearConfirm(), 2000);
124
+ },
125
+
126
+ /** Reset confirm state */
127
+ clearConfirm() {
128
+ if (this._confirmTimer) {
129
+ clearTimeout(this._confirmTimer);
130
+ this._confirmTimer = null;
131
+ }
132
+ if (this._confirmAction && this.element) {
133
+ const btn = this.element.querySelector(`[data-action="${this._confirmAction}"]`);
134
+ if (btn && btn.dataset.origHtml) {
135
+ btn.innerHTML = btn.dataset.origHtml;
136
+ delete btn.dataset.origHtml;
137
+ }
138
+ if (btn) btn.classList.remove('confirming');
139
+ }
140
+ this._confirmAction = null;
141
+ },
142
+
143
+ /** Send a slash command to the active session.
144
+ * Sends text and Enter separately so Ink processes them as distinct events. */
145
+ sendCommand(command) {
146
+ if (!app.activeSessionId) return;
147
+ // Send command text first (without Enter)
148
+ app.sendInput(command);
149
+ // Send Enter separately after a brief delay so Ink has time to process the text.
150
+ setTimeout(() => app.sendInput('\r'), 120);
151
+ },
152
+
153
+ /** Send a special key (arrow, escape, etc.) directly to the PTY.
154
+ * Bypasses tmux send-keys -l (literal mode) since escape sequences
155
+ * must be written raw to be interpreted as key presses by Ink. */
156
+ sendKey(escapeSequence) {
157
+ if (!app.activeSessionId) return;
158
+ fetch(`/api/sessions/${app.activeSessionId}/input`, {
159
+ method: 'POST',
160
+ headers: { 'Content-Type': 'application/json' },
161
+ body: JSON.stringify({ input: escapeSequence })
162
+ }).catch(() => {});
163
+ },
164
+
165
+ /** Read clipboard and send contents as input */
166
+ /** Show a paste overlay with a textarea for iOS compatibility */
167
+ pasteFromClipboard() {
168
+ if (typeof app === 'undefined' || !app.activeSessionId) return;
169
+
170
+ // Create overlay
171
+ const overlay = document.createElement('div');
172
+ overlay.className = 'paste-overlay';
173
+ overlay.innerHTML = `
174
+ <div class="paste-dialog">
175
+ <textarea class="paste-textarea" placeholder="Long-press here and tap Paste"></textarea>
176
+ <div class="paste-actions">
177
+ <button class="paste-cancel">Cancel</button>
178
+ <button class="paste-send">Send</button>
179
+ </div>
180
+ </div>
181
+ `;
182
+
183
+ const textarea = overlay.querySelector('.paste-textarea');
184
+ const send = () => {
185
+ const text = textarea.value;
186
+ overlay.remove();
187
+ if (text) app.sendInput(text);
188
+ };
189
+ overlay.querySelector('.paste-cancel').addEventListener('click', () => overlay.remove());
190
+ overlay.querySelector('.paste-send').addEventListener('click', send);
191
+ overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); });
192
+
193
+ document.body.appendChild(overlay);
194
+ textarea.focus();
195
+ },
196
+
197
+ /** Show the accessory bar */
198
+ show() {
199
+ if (this.element) {
200
+ this.element.classList.add('visible');
201
+ }
202
+ },
203
+
204
+ /** Hide the accessory bar */
205
+ hide() {
206
+ if (this.element) {
207
+ this.element.classList.remove('visible');
208
+ }
209
+ }
210
+ };
211
+
212
+ // ============================================================================
213
+ // Accessibility: Focus Trap for Modals
214
+ // ============================================================================
215
+
216
+ /**
217
+ * FocusTrap - Traps keyboard focus within an element (typically a modal).
218
+ * Saves the previously focused element and restores focus when deactivated.
219
+ */
220
+ class FocusTrap {
221
+ constructor(element) {
222
+ this.element = element;
223
+ this.previouslyFocused = null;
224
+ this.boundHandleKeydown = this.handleKeydown.bind(this);
225
+ }
226
+
227
+ activate() {
228
+ this.previouslyFocused = document.activeElement;
229
+ this.element.addEventListener('keydown', this.boundHandleKeydown);
230
+
231
+ // Focus first focusable element after a brief delay (for CSS transitions)
232
+ requestAnimationFrame(() => {
233
+ const focusable = this.getFocusableElements();
234
+ if (focusable.length) {
235
+ focusable[0].focus();
236
+ }
237
+ });
238
+ }
239
+
240
+ deactivate() {
241
+ this.element.removeEventListener('keydown', this.boundHandleKeydown);
242
+ if (this.previouslyFocused && typeof this.previouslyFocused.focus === 'function') {
243
+ this.previouslyFocused.focus();
244
+ }
245
+ }
246
+
247
+ getFocusableElements() {
248
+ const selector = [
249
+ 'button:not([disabled]):not([tabindex="-1"])',
250
+ 'input:not([disabled]):not([tabindex="-1"])',
251
+ 'select:not([disabled]):not([tabindex="-1"])',
252
+ 'textarea:not([disabled]):not([tabindex="-1"])',
253
+ 'a[href]:not([tabindex="-1"])',
254
+ '[tabindex]:not([tabindex="-1"]):not([disabled])'
255
+ ].join(', ');
256
+
257
+ return [...this.element.querySelectorAll(selector)].filter(
258
+ el => el.offsetParent !== null // Exclude hidden elements
259
+ );
260
+ }
261
+
262
+ handleKeydown(e) {
263
+ if (e.key !== 'Tab') return;
264
+
265
+ const focusable = this.getFocusableElements();
266
+ if (focusable.length === 0) return;
267
+
268
+ const first = focusable[0];
269
+ const last = focusable[focusable.length - 1];
270
+
271
+ if (e.shiftKey && document.activeElement === first) {
272
+ e.preventDefault();
273
+ last.focus();
274
+ } else if (!e.shiftKey && document.activeElement === last) {
275
+ e.preventDefault();
276
+ first.focus();
277
+ }
278
+ }
279
+ }