create-walle 0.9.21 → 0.9.23

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 (500) hide show
  1. package/README.md +27 -5
  2. package/package.json +2 -2
  3. package/template/CLAUDE.md +2 -2
  4. package/template/LICENSE +1 -1
  5. package/template/bin/ctm-dev-cleanup.js +24 -3
  6. package/template/bin/ctm-launch.sh +13 -0
  7. package/template/bin/dev.sh +156 -18
  8. package/template/bin/node-bin.sh +84 -0
  9. package/template/bin/pin-node.sh +51 -0
  10. package/template/claude-task-manager/api-prompts.js +1203 -182
  11. package/template/claude-task-manager/api-reviews.js +109 -15
  12. package/template/claude-task-manager/approval-agent.js +1360 -280
  13. package/template/claude-task-manager/bin/restart-ctm.sh +64 -23
  14. package/template/claude-task-manager/bin/storage-migration-supervisor.js +338 -0
  15. package/template/claude-task-manager/db.js +4417 -295
  16. package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
  17. package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
  18. package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
  19. package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
  20. package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
  21. package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
  22. package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
  23. package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
  24. package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
  25. package/template/claude-task-manager/docs/image-paste-ux.md +13 -0
  26. package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
  27. package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
  28. package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
  29. package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
  30. package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
  31. package/template/claude-task-manager/docs/phone-access-design.md +53 -15
  32. package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
  33. package/template/claude-task-manager/docs/phone-setup.md +3 -0
  34. package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
  35. package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
  36. package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
  37. package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
  38. package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
  39. package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
  40. package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
  41. package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
  42. package/template/claude-task-manager/docs/session-title-authority.md +32 -0
  43. package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
  44. package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
  45. package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
  46. package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
  47. package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
  48. package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
  49. package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
  50. package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
  51. package/template/claude-task-manager/git-utils.js +897 -27
  52. package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
  53. package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
  54. package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
  55. package/template/claude-task-manager/lib/agent-presets.js +17 -1
  56. package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
  57. package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
  58. package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
  59. package/template/claude-task-manager/lib/async-semaphore.js +44 -0
  60. package/template/claude-task-manager/lib/auth-context.js +5 -0
  61. package/template/claude-task-manager/lib/auth-rate-limit.js +47 -4
  62. package/template/claude-task-manager/lib/auth-rules.js +29 -2
  63. package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
  64. package/template/claude-task-manager/lib/background-llm.js +144 -17
  65. package/template/claude-task-manager/lib/branch-inventory.js +212 -0
  66. package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
  67. package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
  68. package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
  69. package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
  70. package/template/claude-task-manager/lib/codex-zst.js +124 -0
  71. package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
  72. package/template/claude-task-manager/lib/connection-health.js +232 -0
  73. package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
  74. package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
  75. package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
  76. package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
  77. package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
  78. package/template/claude-task-manager/lib/document-review.js +141 -6
  79. package/template/claude-task-manager/lib/escalation-review.js +152 -0
  80. package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
  81. package/template/claude-task-manager/lib/headless-term-service.js +678 -0
  82. package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
  83. package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
  84. package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
  85. package/template/claude-task-manager/lib/main-db-census.js +216 -0
  86. package/template/claude-task-manager/lib/message-pagination.js +106 -4
  87. package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +750 -26
  88. package/template/claude-task-manager/lib/mobile-auth-api.js +274 -7
  89. package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
  90. package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
  91. package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
  92. package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
  93. package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
  94. package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
  95. package/template/claude-task-manager/lib/perf-tracker.js +242 -6
  96. package/template/claude-task-manager/lib/permission-match.js +76 -0
  97. package/template/claude-task-manager/lib/permission-sync.js +133 -20
  98. package/template/claude-task-manager/lib/process-title.js +35 -0
  99. package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
  100. package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
  101. package/template/claude-task-manager/lib/prompt-intent.js +132 -0
  102. package/template/claude-task-manager/lib/provider-user-context.js +34 -0
  103. package/template/claude-task-manager/lib/read-pool-client.js +313 -0
  104. package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
  105. package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
  106. package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
  107. package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
  108. package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
  109. package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
  110. package/template/claude-task-manager/lib/restart-guard.js +109 -0
  111. package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
  112. package/template/claude-task-manager/lib/restore-policy.js +13 -0
  113. package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
  114. package/template/claude-task-manager/lib/restore-runtime.js +68 -0
  115. package/template/claude-task-manager/lib/restore-storm.js +34 -0
  116. package/template/claude-task-manager/lib/resume-cwd.js +36 -0
  117. package/template/claude-task-manager/lib/resume-preflight.js +313 -0
  118. package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
  119. package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
  120. package/template/claude-task-manager/lib/scheduler.js +21 -1
  121. package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
  122. package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
  123. package/template/claude-task-manager/lib/server-listeners.js +239 -0
  124. package/template/claude-task-manager/lib/session-capture.js +42 -7
  125. package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
  126. package/template/claude-task-manager/lib/session-history.js +388 -43
  127. package/template/claude-task-manager/lib/session-host-manager.js +287 -0
  128. package/template/claude-task-manager/lib/session-image-refs.js +209 -0
  129. package/template/claude-task-manager/lib/session-jobs.js +399 -59
  130. package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
  131. package/template/claude-task-manager/lib/session-restore.js +53 -0
  132. package/template/claude-task-manager/lib/session-standup.js +123 -23
  133. package/template/claude-task-manager/lib/session-state-bus.js +14 -0
  134. package/template/claude-task-manager/lib/session-stream.js +64 -16
  135. package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
  136. package/template/claude-task-manager/lib/session-token-usage.js +494 -0
  137. package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
  138. package/template/claude-task-manager/lib/setup-network-config.js +9 -0
  139. package/template/claude-task-manager/lib/size-cap.js +45 -0
  140. package/template/claude-task-manager/lib/size-cap.test.js +62 -0
  141. package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
  142. package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
  143. package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
  144. package/template/claude-task-manager/lib/standup-attention.js +7 -3
  145. package/template/claude-task-manager/lib/status-authority.js +39 -0
  146. package/template/claude-task-manager/lib/status-hooks.js +4 -0
  147. package/template/claude-task-manager/lib/storage-migration.js +235 -0
  148. package/template/claude-task-manager/lib/structured-capture.js +298 -0
  149. package/template/claude-task-manager/lib/sync-io-census.js +163 -0
  150. package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
  151. package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
  152. package/template/claude-task-manager/lib/terminal-choice.js +364 -0
  153. package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
  154. package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
  155. package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
  156. package/template/claude-task-manager/lib/timeline-order.js +122 -0
  157. package/template/claude-task-manager/lib/transcript-store.js +348 -43
  158. package/template/claude-task-manager/lib/transport-security.js +84 -1
  159. package/template/claude-task-manager/lib/wait-state.js +184 -0
  160. package/template/claude-task-manager/lib/walle-client.js +47 -5
  161. package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
  162. package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
  163. package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
  164. package/template/claude-task-manager/lib/walle-native-health.js +403 -0
  165. package/template/claude-task-manager/lib/walle-repair.js +701 -0
  166. package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
  167. package/template/claude-task-manager/lib/walle-session-context.js +57 -21
  168. package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
  169. package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
  170. package/template/claude-task-manager/lib/walle-transcript.js +52 -0
  171. package/template/claude-task-manager/lib/worktree-active-sync.js +11 -7
  172. package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
  173. package/template/claude-task-manager/package.json +1 -1
  174. package/template/claude-task-manager/prompt-harvest.js +89 -66
  175. package/template/claude-task-manager/providers/claude-code.js +51 -3
  176. package/template/claude-task-manager/providers/cursor.js +140 -45
  177. package/template/claude-task-manager/public/css/reviews.css +551 -61
  178. package/template/claude-task-manager/public/css/setup.css +191 -0
  179. package/template/claude-task-manager/public/css/walle-session.css +865 -10
  180. package/template/claude-task-manager/public/css/walle.css +154 -0
  181. package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
  182. package/template/claude-task-manager/public/index.html +18516 -2058
  183. package/template/claude-task-manager/public/ipad.html +363 -0
  184. package/template/claude-task-manager/public/js/document-review-links.js +301 -0
  185. package/template/claude-task-manager/public/js/image-normalize.js +69 -36
  186. package/template/claude-task-manager/public/js/message-renderer.js +1265 -77
  187. package/template/claude-task-manager/public/js/prompts.js +66 -29
  188. package/template/claude-task-manager/public/js/reviews.js +901 -133
  189. package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
  190. package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
  191. package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
  192. package/template/claude-task-manager/public/js/setup.js +1273 -176
  193. package/template/claude-task-manager/public/js/stream-view.js +691 -73
  194. package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
  195. package/template/claude-task-manager/public/js/walle-session.js +2455 -158
  196. package/template/claude-task-manager/public/js/walle.js +455 -28
  197. package/template/claude-task-manager/public/m/app.css +2909 -262
  198. package/template/claude-task-manager/public/m/app.js +6601 -398
  199. package/template/claude-task-manager/public/m/claim.html +224 -17
  200. package/template/claude-task-manager/public/m/index.html +117 -21
  201. package/template/claude-task-manager/public/m/sw.js +3 -1
  202. package/template/claude-task-manager/public/manifest.json +2 -2
  203. package/template/claude-task-manager/public/prompts.html +30 -14
  204. package/template/claude-task-manager/queue-engine.js +507 -28
  205. package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
  206. package/template/claude-task-manager/server.js +14341 -2197
  207. package/template/claude-task-manager/session-integrity.js +160 -18
  208. package/template/claude-task-manager/session-search-ranking.js +1 -0
  209. package/template/claude-task-manager/session-utils.js +25 -5
  210. package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
  211. package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
  212. package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
  213. package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
  214. package/template/claude-task-manager/workers/harvest-worker.js +9 -55
  215. package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
  216. package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
  217. package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
  218. package/template/claude-task-manager/workers/session-host-process.js +146 -0
  219. package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
  220. package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
  221. package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
  222. package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
  223. package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
  224. package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
  225. package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
  226. package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
  227. package/template/docs/design/markdown-review-pane.md +206 -0
  228. package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +129 -38
  229. package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
  230. package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
  231. package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
  232. package/template/docs/private-memory-and-pii-policy.md +69 -0
  233. package/template/package.json +2 -1
  234. package/template/scripts/check-private-data.js +201 -0
  235. package/template/shared/sqlite-owner-guard.js +30 -0
  236. package/template/shared/sqlite-owner-write-queue.js +225 -0
  237. package/template/shared/sqlite-storage-policy.js +111 -0
  238. package/template/shared/sqlite-write-lock.js +428 -0
  239. package/template/wall-e/agent-runners/claude-code.js +5 -0
  240. package/template/wall-e/agent.js +166 -22
  241. package/template/wall-e/api-walle.js +524 -70
  242. package/template/wall-e/auth/provider-flows.js +11 -1
  243. package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
  244. package/template/wall-e/brain.js +1614 -141
  245. package/template/wall-e/chat/attachment-blocks.js +96 -0
  246. package/template/wall-e/chat/attachments.js +2 -1
  247. package/template/wall-e/chat/capability-resolver.js +7 -7
  248. package/template/wall-e/chat/context-messages.js +28 -0
  249. package/template/wall-e/chat/conversation-frame.js +630 -0
  250. package/template/wall-e/chat/provider-messages.js +125 -0
  251. package/template/wall-e/chat.js +1002 -233
  252. package/template/wall-e/coding/acceptance-contract.js +170 -0
  253. package/template/wall-e/coding/acp-adapter.js +1 -1
  254. package/template/wall-e/coding/agent-catalog.js +3 -0
  255. package/template/wall-e/coding/artifact-store.js +93 -0
  256. package/template/wall-e/coding/capability-router.js +120 -0
  257. package/template/wall-e/coding/coding-run-controller.js +423 -0
  258. package/template/wall-e/coding/compaction-service.js +157 -12
  259. package/template/wall-e/coding/frontend-verification.js +258 -0
  260. package/template/wall-e/coding/lifecycle-hooks.js +75 -0
  261. package/template/wall-e/coding/local-preview-contract.js +157 -0
  262. package/template/wall-e/coding/permission-service.js +57 -13
  263. package/template/wall-e/coding/prompt-bundle.js +19 -1
  264. package/template/wall-e/coding/prompt-section-registry.js +227 -0
  265. package/template/wall-e/coding/provider-compat.js +15 -0
  266. package/template/wall-e/coding/runtime-events.js +224 -0
  267. package/template/wall-e/coding/runtime-mode.js +3 -0
  268. package/template/wall-e/coding/side-git-snapshot.js +160 -4
  269. package/template/wall-e/coding/snapshot-service.js +143 -1
  270. package/template/wall-e/coding/stream-processor.js +388 -34
  271. package/template/wall-e/coding/task-tool.js +141 -4
  272. package/template/wall-e/coding/tool-execution-controller.js +365 -0
  273. package/template/wall-e/coding/tool-registry.js +43 -5
  274. package/template/wall-e/coding/user-hooks.js +217 -0
  275. package/template/wall-e/coding-orchestrator.js +1330 -221
  276. package/template/wall-e/coding-prompts.js +20 -4
  277. package/template/wall-e/context/context-builder.js +15 -2
  278. package/template/wall-e/decision/confidence.js +1 -1
  279. package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
  280. package/template/wall-e/docs/external-action-controller.md +26 -6
  281. package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
  282. package/template/wall-e/embeddings.js +591 -53
  283. package/template/wall-e/external-action-controller.js +12 -0
  284. package/template/wall-e/http/auth.js +1 -0
  285. package/template/wall-e/http/chat-api.js +46 -11
  286. package/template/wall-e/http/model-admin.js +836 -34
  287. package/template/wall-e/lib/boot-profile.js +88 -0
  288. package/template/wall-e/lib/event-loop-monitor.js +93 -0
  289. package/template/wall-e/lib/service-health.js +194 -0
  290. package/template/wall-e/llm/anthropic.js +130 -5
  291. package/template/wall-e/llm/client.js +266 -63
  292. package/template/wall-e/llm/default-fallback.js +382 -0
  293. package/template/wall-e/llm/health.js +19 -0
  294. package/template/wall-e/llm/message-guard.js +78 -0
  295. package/template/wall-e/llm/model-catalog.js +252 -1
  296. package/template/wall-e/llm/openai.js +26 -4
  297. package/template/wall-e/llm/portkey-sync.js +654 -0
  298. package/template/wall-e/llm/provider-error.js +30 -2
  299. package/template/wall-e/llm/registry.js +5 -1
  300. package/template/wall-e/llm/request-compat.js +67 -0
  301. package/template/wall-e/loops/backfill.js +79 -23
  302. package/template/wall-e/loops/brain-optimize.js +67 -0
  303. package/template/wall-e/loops/ingest.js +25 -10
  304. package/template/wall-e/loops/question-digest.js +160 -0
  305. package/template/wall-e/loops/reflect.js +6 -4
  306. package/template/wall-e/loops/think.js +39 -12
  307. package/template/wall-e/mcp-server.js +318 -36
  308. package/template/wall-e/memory/ctm-context-client.js +52 -14
  309. package/template/wall-e/memory/ctm-operational-context.js +237 -0
  310. package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
  311. package/template/wall-e/memory/ctm-session-context.js +111 -63
  312. package/template/wall-e/prompts/coding/deepseek.txt +3 -0
  313. package/template/wall-e/prompts/coding/gemini.txt +6 -0
  314. package/template/wall-e/prompts/coding/gpt.txt +6 -0
  315. package/template/wall-e/prompts/coding/local.txt +7 -0
  316. package/template/wall-e/runtime/decision-hooks.js +115 -0
  317. package/template/wall-e/runtime/devbox-gateway.js +82 -8
  318. package/template/wall-e/runtime/prompt-manifest.js +86 -0
  319. package/template/wall-e/runtime/tool-executor.js +269 -0
  320. package/template/wall-e/runtime/tool-result-envelope.js +138 -0
  321. package/template/wall-e/runtime/transcript-projection.js +60 -0
  322. package/template/wall-e/runtime/walle-runtime.js +224 -0
  323. package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
  324. package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
  325. package/template/wall-e/server.js +15 -0
  326. package/template/wall-e/session-files.js +9 -0
  327. package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
  328. package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
  329. package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
  330. package/template/wall-e/skills/claude-code-reader.js +7 -3
  331. package/template/wall-e/skills/script-skill-runner.js +10 -0
  332. package/template/wall-e/skills/skill-planner.js +38 -0
  333. package/template/wall-e/tools/builtin-middleware.js +19 -9
  334. package/template/wall-e/tools/local-tools.js +1428 -16
  335. package/template/wall-e/tools/permission-checker.js +73 -5
  336. package/template/wall-e/tools/question-manager.js +117 -7
  337. package/template/wall-e/training/harvester.js +12 -28
  338. package/template/wall-e/training/replay.js +25 -80
  339. package/template/website/index.html +10 -10
  340. package/template/wall-e/eval/ab-test.js +0 -203
  341. package/template/wall-e/eval/agent-runner.js +0 -772
  342. package/template/wall-e/eval/agent-scorer.js +0 -461
  343. package/template/wall-e/eval/aggregator.js +0 -414
  344. package/template/wall-e/eval/allowed-test-commands.js +0 -34
  345. package/template/wall-e/eval/benchmark-generator.js +0 -113
  346. package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
  347. package/template/wall-e/eval/benchmarks/chat.json +0 -82
  348. package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
  349. package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
  350. package/template/wall-e/eval/benchmarks/coding.json +0 -122
  351. package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
  352. package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
  353. package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
  354. package/template/wall-e/eval/benchmarks.js +0 -669
  355. package/template/wall-e/eval/cc-replay.js +0 -719
  356. package/template/wall-e/eval/chat-eval.js +0 -525
  357. package/template/wall-e/eval/check-keys.js +0 -15
  358. package/template/wall-e/eval/check-providers.js +0 -42
  359. package/template/wall-e/eval/codex-cli-baseline.js +0 -669
  360. package/template/wall-e/eval/coding-agent-real.js +0 -570
  361. package/template/wall-e/eval/context-compactor.js +0 -251
  362. package/template/wall-e/eval/debug-agent003.js +0 -68
  363. package/template/wall-e/eval/diagnostics.js +0 -216
  364. package/template/wall-e/eval/eval-orchestrator.js +0 -642
  365. package/template/wall-e/eval/evaluate.js +0 -202
  366. package/template/wall-e/eval/evaluator.js +0 -373
  367. package/template/wall-e/eval/exporter.js +0 -212
  368. package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
  369. package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
  370. package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
  371. package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
  372. package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
  373. package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
  374. package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
  375. package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
  376. package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
  377. package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
  378. package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
  379. package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
  380. package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
  381. package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
  382. package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
  383. package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
  384. package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
  385. package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
  386. package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
  387. package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
  388. package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
  389. package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
  390. package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
  391. package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
  392. package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
  393. package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
  394. package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
  395. package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
  396. package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
  397. package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
  398. package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
  399. package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
  400. package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
  401. package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
  402. package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
  403. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
  404. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
  405. package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
  406. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
  407. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
  408. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
  409. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
  410. package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
  411. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
  412. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
  413. package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
  414. package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
  415. package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
  416. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
  417. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
  418. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
  419. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
  420. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
  421. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
  422. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
  423. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
  424. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
  425. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
  426. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
  427. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
  428. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
  429. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
  430. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
  431. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
  432. package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
  433. package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
  434. package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
  435. package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
  436. package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
  437. package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
  438. package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
  439. package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
  440. package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
  441. package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
  442. package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
  443. package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
  444. package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
  445. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
  446. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
  447. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
  448. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
  449. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
  450. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
  451. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
  452. package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
  453. package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
  454. package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
  455. package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
  456. package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
  457. package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
  458. package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
  459. package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
  460. package/template/wall-e/eval/harvester.js +0 -685
  461. package/template/wall-e/eval/head-to-head.js +0 -388
  462. package/template/wall-e/eval/humaneval-adapter.js +0 -321
  463. package/template/wall-e/eval/list-models.js +0 -31
  464. package/template/wall-e/eval/livecodebench-adapter.js +0 -291
  465. package/template/wall-e/eval/mail-integration.js +0 -443
  466. package/template/wall-e/eval/manifest.js +0 -186
  467. package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
  468. package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
  469. package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
  470. package/template/wall-e/eval/meta-harness/cli.js +0 -86
  471. package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
  472. package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
  473. package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
  474. package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
  475. package/template/wall-e/eval/meta-harness/frontier.js +0 -96
  476. package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
  477. package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
  478. package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
  479. package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
  480. package/template/wall-e/eval/meta-harness/reporting.js +0 -58
  481. package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
  482. package/template/wall-e/eval/meta-harness/validation.js +0 -81
  483. package/template/wall-e/eval/promoter.js +0 -228
  484. package/template/wall-e/eval/provider-normalizer.js +0 -33
  485. package/template/wall-e/eval/replay.js +0 -395
  486. package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
  487. package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
  488. package/template/wall-e/eval/run-coding-agent-real.js +0 -187
  489. package/template/wall-e/eval/run-eval.js +0 -435
  490. package/template/wall-e/eval/run-model-comparison.js +0 -142
  491. package/template/wall-e/eval/session-evaluator.js +0 -187
  492. package/template/wall-e/eval/session-miner.js +0 -207
  493. package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
  494. package/template/wall-e/eval/session-transcripts.js +0 -509
  495. package/template/wall-e/eval/shadow.js +0 -161
  496. package/template/wall-e/eval/swebench-adapter.js +0 -345
  497. package/template/wall-e/eval/swebench-docker.js +0 -192
  498. package/template/wall-e/eval/train.py +0 -320
  499. package/template/wall-e/eval/trainer.js +0 -232
  500. package/template/wall-e/eval/weekly-eval-loop.js +0 -241
@@ -0,0 +1,678 @@
1
+ // --- Headless Terminal Worker Thread ---
2
+ // Maintains a @xterm/headless Terminal instance per session (no renderer cost).
3
+ // Replaces vterm-worker.js: full xterm.js parser is more accurate for ANSI
4
+ // parsing than the custom VTermScreen, and the serialize addon enables instant
5
+ // terminal state snapshots for tab-switch restore.
6
+ //
7
+ // Message protocol:
8
+ // Main → Worker:
9
+ // { type: 'create', sessionId, cols, rows }
10
+ // { type: 'feed', sessionId, data, requestId? }
11
+ // { type: 'resize', sessionId, cols, rows }
12
+ // { type: 'serialize', sessionId, requestId, scrollback? }
13
+ // { type: 'fingerprint', sessionId, requestId, cols, rows }
14
+ // { type: 'destroy', sessionId }
15
+ // { type: 'reset', sessionId }
16
+ // { type: 'recheck', sessionId, requestId }
17
+ // { type: 'getText', sessionId, requestId }
18
+ // { type: 'getViewportText', sessionId, requestId }
19
+ // { type: 'getRows', sessionId, requestId }
20
+ //
21
+ // Worker → Main:
22
+ // { type: 'feed-result', sessionId, requestId, hasApproval, text?, rawDetected?, rawText?, debugLines }
23
+ // { type: 'serialized', sessionId, requestId, data, cols, rows, fp }
24
+ // { type: 'fingerprinted', sessionId, requestId, fp, cols, rows, scrollbackLen }
25
+ // { type: 'text', sessionId, requestId, text }
26
+ // { type: 'rows', sessionId, requestId, rows: [{ text, wrapped }] }
27
+
28
+ const { Terminal } = require('@xterm/headless');
29
+ const { SerializeAddon } = require('@xterm/addon-serialize');
30
+ const { fingerprintRows, EMPTY_FINGERPRINT } = require('./terminal-fingerprint');
31
+
32
+ const { detectApproval, detectApprovalWithProvider } = require('../workers/approval-patterns');
33
+ const { validateWidget } = require('../workers/approval-widget-validator');
34
+ const {
35
+ MAX_SNAPSHOT_SCROLLBACK_ROWS,
36
+ normalizeSnapshotScrollbackRows,
37
+ } = require('./terminal-snapshot-options');
38
+
39
+ let postMessage = () => {};
40
+
41
+ // Phase 1 temporal thresholds. Tuned empirically: Claude Code stops
42
+ // streaming output ~50ms before showing the prompt; cursor settles within
43
+ // a few ms of the final widget draw. We require a margin.
44
+ const CURSOR_STABLE_MS = 200;
45
+ const OUTPUT_QUIESCENT_MS = 100;
46
+ const DIRECT_APPROVAL_SCAN_INTERVAL_MS = 150;
47
+
48
+ // Phase 3 cooldown: if the post-keystroke verification fails, we disable
49
+ // auto-approval for this session for this window. Prevents a chain of
50
+ // false keystrokes from a persistent false positive.
51
+ const VERIFY_FAIL_COOLDOWN_MS = 30 * 1000;
52
+
53
+ // Per-session headless terminal instances + state
54
+ const terminals = new Map(); // sessionId -> { term, serialize, state }
55
+
56
+ function getOrCreate(sessionId, cols = 120, rows = 30) {
57
+ let entry = terminals.get(sessionId);
58
+ if (!entry) {
59
+ const term = new Terminal({
60
+ cols,
61
+ rows,
62
+ scrollback: MAX_SNAPSHOT_SCROLLBACK_ROWS,
63
+ allowProposedApi: true,
64
+ });
65
+ const serialize = new SerializeAddon();
66
+ term.loadAddon(serialize);
67
+ entry = {
68
+ term,
69
+ serialize,
70
+ state: {
71
+ cursorY: -1, cursorX: -1,
72
+ lastCursorMoveAt: 0,
73
+ lastOutputAt: 0,
74
+ nextDirectApprovalScanAt: 0,
75
+ // Phase 3 verification: set by main thread after it fires a keystroke;
76
+ // worker skips emitting approval-alert while this is in the future.
77
+ verifyUntil: 0,
78
+ // Phase 3 cooldown: set when verification fails; skip all detection
79
+ cooldownUntil: 0,
80
+ },
81
+ };
82
+ terminals.set(sessionId, entry);
83
+ }
84
+ return entry;
85
+ }
86
+
87
+ /**
88
+ * Snapshot the current cursor position; update stability timestamps.
89
+ * Called after each feed. Returns the state object.
90
+ */
91
+ function _trackCursor(entry) {
92
+ const now = Date.now();
93
+ const buf = entry.term.buffer.active;
94
+ const y = buf.cursorY;
95
+ const x = buf.cursorX;
96
+ const s = entry.state;
97
+ if (s.cursorY !== y || s.cursorX !== x) {
98
+ s.cursorY = y;
99
+ s.cursorX = x;
100
+ s.lastCursorMoveAt = now;
101
+ }
102
+ s.lastOutputAt = now;
103
+ return s;
104
+ }
105
+
106
+ /**
107
+ * Phase 1 temporal gate: cursor stable, output quiescent.
108
+ */
109
+ function _temporalGateOk(state) {
110
+ const now = Date.now();
111
+ const cursorAge = now - state.lastCursorMoveAt;
112
+ const outputAge = now - state.lastOutputAt;
113
+ return cursorAge >= CURSOR_STABLE_MS && outputAge >= OUTPUT_QUIESCENT_MS;
114
+ }
115
+
116
+ /**
117
+ * Full gate check — combines Phase 1 temporal + Phase 2 structural +
118
+ * Phase 3 cooldown. Returns { ok, reason }.
119
+ *
120
+ * The order matters for debugging: cheap checks first.
121
+ */
122
+ function _gateApproval(entry, _text) {
123
+ const { state, term } = entry;
124
+ const now = Date.now();
125
+
126
+ // Phase 3: cooldown after verification failure
127
+ if (state.cooldownUntil > now) {
128
+ return { ok: false, reason: 'cooldown' };
129
+ }
130
+
131
+ // Phase 3: in the verification window from a previous keystroke — skip
132
+ if (state.verifyUntil > now) {
133
+ return { ok: false, reason: 'verify-window' };
134
+ }
135
+
136
+ // Phase 1: temporal gate (note: temporal is a SOFT signal — we still
137
+ // require structural widget detection below, so temporal failure alone
138
+ // doesn't reject; we log it as a signal)
139
+ // TEMPORAL IS DISABLED IN RECHECK PATH because the recheck fires on a
140
+ // timer with no fresh output — both ages would always pass. Keep the
141
+ // temporal check advisory only; structural is the primary gate.
142
+ // const temporalOk = _temporalGateOk(state);
143
+
144
+ // Phase 1+2: structural widget validation. Requires alt-screen active,
145
+ // "1. Yes" in bottom rows, widget-like formatting (color or ❯).
146
+ const widget = validateWidget(term);
147
+ if (!widget.valid) {
148
+ return { ok: false, reason: `widget:${widget.reason}` };
149
+ }
150
+
151
+ return { ok: true, reason: 'ok' };
152
+ }
153
+
154
+ // Throttle log spam — one line per (session, reason) per 10s.
155
+ const _gateMissLog = new Map(); // sessionId -> { reason -> lastLoggedAt }
156
+ function _debugLinesFromText(text) {
157
+ return String(text || '')
158
+ .split('\n')
159
+ .filter(l => l.trim())
160
+ .slice(-3)
161
+ .map(l => l.trim().slice(0, 80));
162
+ }
163
+
164
+ function _logGateMiss(sessionId, reason, details = {}) {
165
+ const now = Date.now();
166
+ let bySession = _gateMissLog.get(sessionId);
167
+ if (!bySession) { bySession = new Map(); _gateMissLog.set(sessionId, bySession); }
168
+ const last = bySession.get(reason) || 0;
169
+ if (now - last < 10000) return;
170
+ bySession.set(reason, now);
171
+ // Emit a diagnostic to the main thread — main thread logs with session tag
172
+ postMessage({
173
+ type: 'gate-miss',
174
+ sessionId,
175
+ reason,
176
+ source: details.source || 'realtime',
177
+ providerId: details.providerId || null,
178
+ rawDetected: !!details.rawDetected,
179
+ rawText: details.rawText || null,
180
+ debugLines: details.debugLines || _debugLinesFromText(details.rawText || ''),
181
+ });
182
+ }
183
+
184
+ function _approvalOutputHasHint(text) {
185
+ if (!text) return false;
186
+ return /do you want|proceed|approve|approval|permission|allow|\byes\b|\bno\b|run this command|bash|edit|tool use|❯|1\.\s*yes|2\.\s*no/i.test(String(text));
187
+ }
188
+
189
+ // Extract visible text from the terminal buffer (for approval detection).
190
+ // Reads the viewport plus a window of scrollback above it so that prompts
191
+ // taller than the viewport (e.g. a Codex prompt with a long heredoc command)
192
+ // still expose their question/reason/command lines — those scroll above
193
+ // viewportY but are still in `buffer.active` for as long as scrollback holds
194
+ // them. Without the scrollback window, only the bottom 3-5 lines of the
195
+ // option list survive and parsers like codex.parse() fail.
196
+ //
197
+ // 80 rows = ~2 viewports of headroom on a typical 30-row terminal. Wider
198
+ // than any Codex prompt observed in production while bounding the scan cost.
199
+ const APPROVAL_SCROLLBACK_ROWS = 80;
200
+ function getVisibleText(term) {
201
+ const buf = term.buffer.active;
202
+ const lines = [];
203
+ const start = Math.max(0, buf.viewportY - APPROVAL_SCROLLBACK_ROWS);
204
+ const end = buf.viewportY + term.rows;
205
+ for (let i = start; i < end; i++) {
206
+ const line = buf.getLine(i);
207
+ if (line) lines.push(line.translateToString(true));
208
+ }
209
+ return lines.join('\n');
210
+ }
211
+
212
+ function getViewportText(term) {
213
+ const buf = term.buffer.active;
214
+ const lines = [];
215
+ const start = buf.viewportY;
216
+ const end = buf.viewportY + term.rows;
217
+ for (let i = start; i < end; i++) {
218
+ const line = buf.getLine(i);
219
+ if (line) lines.push(line.translateToString(true));
220
+ }
221
+ return lines.join('\n');
222
+ }
223
+
224
+ // Fingerprint of the visible grid at the terminal's CURRENT dims. Shared by the
225
+ // `serialize` and `fingerprint` ops so a snapshot can carry its own content fp and
226
+ // the client can skip a destructive re-render when its buffer already matches.
227
+ function viewportFingerprint(term) {
228
+ const buf = term.buffer.active;
229
+ const rows = term.rows;
230
+ const start = buf.viewportY;
231
+ const rowStrings = [];
232
+ for (let i = 0; i < rows; i++) {
233
+ const line = buf.getLine(start + i);
234
+ rowStrings.push(line ? line.translateToString(true) : '');
235
+ }
236
+ const scrollbackLen = Math.max(0, buf.length - rows);
237
+ return fingerprintRows(rowStrings, { cols: term.cols, rows, scrollbackLen });
238
+ }
239
+
240
+ function getVisibleRows(term) {
241
+ const buf = term.buffer.active;
242
+ const rows = [];
243
+ const start = Math.max(0, buf.viewportY - APPROVAL_SCROLLBACK_ROWS);
244
+ const end = buf.viewportY + term.rows;
245
+ for (let i = start; i < end; i++) {
246
+ const line = buf.getLine(i);
247
+ if (line) {
248
+ rows.push({
249
+ text: line.translateToString(true),
250
+ wrapped: !!line.isWrapped,
251
+ });
252
+ }
253
+ }
254
+ return rows;
255
+ }
256
+
257
+ // Queue for pending writes — headless xterm.js write() is async (uses microtask),
258
+ // so we need to sequence operations that depend on write completion.
259
+ const writeQueues = new Map(); // sessionId -> Promise chain
260
+
261
+ // Bound how long a single xterm.js write can block the queue. In rare cases
262
+ // (memory pressure, specific ANSI sequences that trip an internal bug)
263
+ // `entry.term.write(data, callback)` fails to invoke the callback, which would
264
+ // otherwise permanently stall the session's write chain — and with it, all
265
+ // approval detection (both direct-feed and recheck await the queue). When
266
+ // this fires we force-advance: the terminal state may be slightly stale for
267
+ // this one chunk, but new writes unblock and detection resumes.
268
+ const WRITE_TIMEOUT_MS = 5000;
269
+ // Telemetry: how many times each session tripped the timeout. Surfaced to
270
+ // the main thread via 'write-stall' messages so the UI can show a degraded
271
+ // badge rather than leaving the user guessing why auto-approve is silent.
272
+ const _writeStallCounts = new Map(); // sessionId -> count
273
+
274
+ function enqueueWrite(sessionId, data) {
275
+ const entry = getOrCreate(sessionId);
276
+ const prev = writeQueues.get(sessionId) || Promise.resolve();
277
+ const next = prev.then(() => new Promise(resolve => {
278
+ let settled = false;
279
+ const timer = setTimeout(() => {
280
+ if (settled) return;
281
+ settled = true;
282
+ const count = (_writeStallCounts.get(sessionId) || 0) + 1;
283
+ _writeStallCounts.set(sessionId, count);
284
+ try {
285
+ postMessage({ type: 'write-stall', sessionId, count, timeoutMs: WRITE_TIMEOUT_MS });
286
+ } catch {}
287
+ resolve();
288
+ }, WRITE_TIMEOUT_MS);
289
+ entry.term.write(data, () => {
290
+ if (settled) return;
291
+ settled = true;
292
+ clearTimeout(timer);
293
+ resolve();
294
+ });
295
+ }));
296
+ // Periodically reset the promise chain to prevent unbounded growth.
297
+ // After the write completes, replace the chain with a fresh resolved promise.
298
+ const reset = next.then(() => {
299
+ if (writeQueues.get(sessionId) === reset) writeQueues.set(sessionId, Promise.resolve());
300
+ });
301
+ writeQueues.set(sessionId, reset);
302
+ return next;
303
+ }
304
+
305
+ // Bounded wait for the pending-writes queue before reading terminal state.
306
+ // Routine detection passes intentionally use a short wait so they stay cheap.
307
+ // Authoritative snapshots, especially the final Codex exit snapshot, can opt
308
+ // into a longer bounded wait because they become the persisted terminal truth.
309
+ const QUEUE_WAIT_TIMEOUT_MS = 2000;
310
+ const MAX_QUEUE_WAIT_TIMEOUT_MS = 15000;
311
+ function normalizeQueueWaitTimeoutMs(value) {
312
+ if (!Number.isFinite(Number(value))) return QUEUE_WAIT_TIMEOUT_MS;
313
+ return Math.max(0, Math.min(MAX_QUEUE_WAIT_TIMEOUT_MS, Number(value)));
314
+ }
315
+
316
+ async function _awaitQueueBounded(sessionId, timeoutMs = QUEUE_WAIT_TIMEOUT_MS) {
317
+ const queue = writeQueues.get(sessionId);
318
+ if (!queue) return;
319
+ const boundedTimeoutMs = normalizeQueueWaitTimeoutMs(timeoutMs);
320
+ let timer;
321
+ const timeoutPromise = new Promise(resolve => {
322
+ timer = setTimeout(resolve, boundedTimeoutMs);
323
+ });
324
+ await Promise.race([queue, timeoutPromise]);
325
+ if (timer) clearTimeout(timer);
326
+ }
327
+
328
+ const opQueues = new Map(); // sessionId -> Promise chain for feed/serialize/destroy ordering
329
+
330
+ function enqueueSessionOp(sessionId, fn) {
331
+ const prev = opQueues.get(sessionId) || Promise.resolve();
332
+ let next;
333
+ next = prev
334
+ .catch(() => {})
335
+ .then(fn)
336
+ .catch((err) => {
337
+ try {
338
+ postMessage({
339
+ type: 'worker-error',
340
+ sessionId,
341
+ message: err && err.message ? err.message : String(err),
342
+ });
343
+ } catch {}
344
+ })
345
+ .finally(() => {
346
+ if (opQueues.get(sessionId) === next) opQueues.delete(sessionId);
347
+ });
348
+ opQueues.set(sessionId, next);
349
+ return next;
350
+ }
351
+
352
+ async function handleMessage(msg) {
353
+ switch (msg.type) {
354
+ case 'create': {
355
+ getOrCreate(msg.sessionId, msg.cols, msg.rows);
356
+ break;
357
+ }
358
+
359
+ case 'feed': {
360
+ // Feed data into the headless terminal buffer. Two modes:
361
+ //
362
+ // 1. Direct feed (no requestId): from PTY onData — keeps headless terminal
363
+ // current for snapshots. Also runs lightweight approval detection and sends
364
+ // an immediate 'approval-alert' if found. This catches prompts the instant
365
+ // they render, regardless of main-thread CPU load or throttle state.
366
+ //
367
+ // 2. Throttled feed (has requestId): from the approval engine's recheck cycle.
368
+ // Sends full feed-result with hasApproval flag. Acts as safety-net fallback
369
+ // for prompts missed by the direct-feed path (e.g. multi-chunk rendering).
370
+ await enqueueWrite(msg.sessionId, msg.data);
371
+
372
+ const entry = terminals.get(msg.sessionId);
373
+ if (!entry) break;
374
+
375
+ // Phase 1: track cursor/output movement for temporal gating below.
376
+ // Called for BOTH direct-feed and throttled-feed so state is always fresh.
377
+ _trackCursor(entry);
378
+
379
+ if (msg.requestId == null) {
380
+ // Direct feed — run real-time approval detection.
381
+ if (!_approvalOutputHasHint(msg.data)) break;
382
+ const now = Date.now();
383
+ if (entry.state.nextDirectApprovalScanAt && now < entry.state.nextDirectApprovalScanAt) break;
384
+ entry.state.nextDirectApprovalScanAt = now + DIRECT_APPROVAL_SCAN_INTERVAL_MS;
385
+ const text = getVisibleText(entry.term);
386
+ const { detected, providerId } = detectApprovalWithProvider(text);
387
+ if (!detected) break;
388
+ // Phase 1/2/3 gates — see _gateApproval for reasoning.
389
+ const gate = _gateApproval(entry, text);
390
+ if (!gate.ok) {
391
+ // Log once per reason per session to avoid spam.
392
+ _logGateMiss(msg.sessionId, gate.reason, {
393
+ source: 'realtime',
394
+ providerId,
395
+ rawDetected: true,
396
+ rawText: text,
397
+ });
398
+ break;
399
+ }
400
+ postMessage({
401
+ type: 'approval-alert',
402
+ sessionId: msg.sessionId,
403
+ providerId,
404
+ text,
405
+ });
406
+ break;
407
+ }
408
+
409
+ // Throttled recheck path (safety net)
410
+ const text = getVisibleText(entry.term);
411
+ const { detected: hasApprovalRaw, providerId } = detectApprovalWithProvider(text);
412
+ const hintDetected = _approvalOutputHasHint(text);
413
+ // Apply same gating to the throttled path — hasApproval reflects gated decision.
414
+ let gatedApproval = hasApprovalRaw;
415
+ let gateReason = null;
416
+ if (hasApprovalRaw) {
417
+ const gate = _gateApproval(entry, text);
418
+ if (!gate.ok) { gatedApproval = false; gateReason = gate.reason; }
419
+ }
420
+ const debugLines = _debugLinesFromText(text);
421
+
422
+ postMessage({
423
+ type: 'feed-result',
424
+ sessionId: msg.sessionId,
425
+ requestId: msg.requestId,
426
+ hasApproval: gatedApproval,
427
+ providerId,
428
+ text: gatedApproval ? text : null,
429
+ rawDetected: hasApprovalRaw,
430
+ hintDetected,
431
+ rawText: hasApprovalRaw || hintDetected ? text : null,
432
+ debugLines,
433
+ gateReason,
434
+ });
435
+ break;
436
+ }
437
+
438
+ case 'recheck': {
439
+ // Re-evaluate current screen without feeding new data
440
+ const entry = terminals.get(msg.sessionId);
441
+ if (!entry) {
442
+ // Send empty result so main thread's _workerPending gets cleared
443
+ if (msg.requestId != null) {
444
+ postMessage({
445
+ type: 'feed-result', sessionId: msg.sessionId,
446
+ requestId: msg.requestId, hasApproval: false, providerId: null, text: null,
447
+ rawDetected: false, rawText: null, debugLines: [],
448
+ });
449
+ }
450
+ break;
451
+ }
452
+ // Wait for any pending writes to complete (bounded — do not hang forever
453
+ // on a stuck write queue)
454
+ await _awaitQueueBounded(msg.sessionId);
455
+
456
+ const text = getVisibleText(entry.term);
457
+ const { detected: hasApprovalRaw, providerId } = detectApprovalWithProvider(text);
458
+ const hintDetected = _approvalOutputHasHint(text);
459
+ // Apply full gating on recheck too (structural + cooldown).
460
+ // Temporal gate is skipped here because recheck fires without fresh
461
+ // output — the structural widget validator is the primary signal.
462
+ let hasApproval = hasApprovalRaw;
463
+ let gateReason = null;
464
+ if (hasApprovalRaw) {
465
+ const gate = _gateApproval(entry, text);
466
+ if (!gate.ok) { hasApproval = false; gateReason = gate.reason; }
467
+ }
468
+ const debugLines = _debugLinesFromText(text);
469
+
470
+ postMessage({
471
+ type: 'feed-result',
472
+ sessionId: msg.sessionId,
473
+ requestId: msg.requestId,
474
+ hasApproval,
475
+ providerId,
476
+ text: hasApproval ? text : null,
477
+ rawDetected: hasApprovalRaw,
478
+ hintDetected,
479
+ rawText: hasApprovalRaw || hintDetected ? text : null,
480
+ debugLines,
481
+ gateReason,
482
+ });
483
+ break;
484
+ }
485
+
486
+ case 'verify-start': {
487
+ // Phase 3: main thread just wrote a keystroke. Enter verification
488
+ // window — skip all approval detection until verify-end or window expires.
489
+ const entry = terminals.get(msg.sessionId);
490
+ if (!entry) break;
491
+ entry.state.verifyUntil = Date.now() + (msg.windowMs || 500);
492
+ break;
493
+ }
494
+
495
+ case 'verify-end': {
496
+ // Phase 3: main thread observed outcome. If verification failed
497
+ // (the keystroke produced no state transition), enter cooldown.
498
+ const entry = terminals.get(msg.sessionId);
499
+ if (!entry) break;
500
+ entry.state.verifyUntil = 0;
501
+ if (msg.failed) {
502
+ entry.state.cooldownUntil = Date.now() + VERIFY_FAIL_COOLDOWN_MS;
503
+ }
504
+ break;
505
+ }
506
+
507
+ case 'resize': {
508
+ const entry = terminals.get(msg.sessionId);
509
+ if (entry) entry.term.resize(msg.cols, msg.rows);
510
+ break;
511
+ }
512
+
513
+ case 'serialize': {
514
+ const entry = terminals.get(msg.sessionId);
515
+ if (!entry) {
516
+ postMessage({
517
+ type: 'serialized', sessionId: msg.sessionId,
518
+ requestId: msg.requestId, data: '', cols: 120, rows: 30,
519
+ fp: EMPTY_FINGERPRINT,
520
+ });
521
+ break;
522
+ }
523
+ // Wait for any pending writes to complete before serializing (bounded).
524
+ // Final-exit snapshots pass a longer wait so a large final PTY burst
525
+ // cannot be serialized before xterm has applied it.
526
+ await _awaitQueueBounded(msg.sessionId, msg.queueWaitTimeoutMs);
527
+
528
+ const scrollback = normalizeSnapshotScrollbackRows(msg.scrollback);
529
+ const data = entry.serialize.serialize({ scrollback });
530
+ postMessage({
531
+ type: 'serialized',
532
+ sessionId: msg.sessionId,
533
+ requestId: msg.requestId,
534
+ data,
535
+ cols: entry.term.cols,
536
+ rows: entry.term.rows,
537
+ scrollback,
538
+ fp: viewportFingerprint(entry.term),
539
+ });
540
+ break;
541
+ }
542
+
543
+ case 'fingerprint': {
544
+ const entry = terminals.get(msg.sessionId);
545
+ if (!entry) {
546
+ postMessage({
547
+ type: 'fingerprinted', sessionId: msg.sessionId,
548
+ requestId: msg.requestId, fp: EMPTY_FINGERPRINT,
549
+ cols: Number(msg.cols) || 0, rows: Number(msg.rows) || 0, scrollbackLen: 0,
550
+ });
551
+ break;
552
+ }
553
+ // Wait for pending writes so we fingerprint settled state, not mid-write.
554
+ await _awaitQueueBounded(msg.sessionId, msg.queueWaitTimeoutMs);
555
+ // Measure at the CLIENT's dims so client and server fingerprint the same
556
+ // grid (foreign-width content is real divergence). We resize, sample, then
557
+ // resize BACK so the fingerprint probe never permanently mutates the
558
+ // authoritative dims that serialize()/other ops depend on. Guard against
559
+ // NaN/zero dims (hidden/zero-size client container).
560
+ // NOTE: once the dimension-authority pillar (P2) keeps the headless term
561
+ // at the client's dims at all times, this resize round-trip is a no-op.
562
+ const origCols = entry.term.cols;
563
+ const origRows = entry.term.rows;
564
+ const wantResize = Number.isFinite(msg.cols) && Number.isFinite(msg.rows)
565
+ && msg.cols >= 1 && msg.rows >= 1
566
+ && (origCols !== msg.cols || origRows !== msg.rows);
567
+ if (wantResize) entry.term.resize(msg.cols, msg.rows);
568
+ const sampleCols = entry.term.cols;
569
+ const sampleRows = entry.term.rows;
570
+ const scrollbackLen = Math.max(0, entry.term.buffer.active.length - sampleRows);
571
+ const fp = viewportFingerprint(entry.term);
572
+ if (wantResize) entry.term.resize(origCols, origRows);
573
+ postMessage({
574
+ type: 'fingerprinted', sessionId: msg.sessionId,
575
+ requestId: msg.requestId, fp,
576
+ cols: sampleCols, rows: sampleRows, scrollbackLen,
577
+ });
578
+ break;
579
+ }
580
+
581
+ case 'getText': {
582
+ const entry = terminals.get(msg.sessionId);
583
+ if (!entry) {
584
+ postMessage({ type: 'text', sessionId: msg.sessionId, requestId: msg.requestId, text: '' });
585
+ break;
586
+ }
587
+ await _awaitQueueBounded(msg.sessionId);
588
+ const text = getVisibleText(entry.term);
589
+ postMessage({ type: 'text', sessionId: msg.sessionId, requestId: msg.requestId, text });
590
+ break;
591
+ }
592
+
593
+ case 'getViewportText': {
594
+ const entry = terminals.get(msg.sessionId);
595
+ if (!entry) {
596
+ postMessage({ type: 'viewport-text', sessionId: msg.sessionId, requestId: msg.requestId, text: '' });
597
+ break;
598
+ }
599
+ await _awaitQueueBounded(msg.sessionId);
600
+ const text = getViewportText(entry.term);
601
+ postMessage({ type: 'viewport-text', sessionId: msg.sessionId, requestId: msg.requestId, text });
602
+ break;
603
+ }
604
+
605
+ case 'getRows': {
606
+ const entry = terminals.get(msg.sessionId);
607
+ if (!entry) {
608
+ postMessage({ type: 'rows', sessionId: msg.sessionId, requestId: msg.requestId, rows: [] });
609
+ break;
610
+ }
611
+ await _awaitQueueBounded(msg.sessionId);
612
+ const rows = getVisibleRows(entry.term);
613
+ postMessage({ type: 'rows', sessionId: msg.sessionId, requestId: msg.requestId, rows });
614
+ break;
615
+ }
616
+
617
+ case 'reset': {
618
+ const entry = terminals.get(msg.sessionId);
619
+ if (entry) {
620
+ entry.term.reset();
621
+ writeQueues.delete(msg.sessionId);
622
+ }
623
+ break;
624
+ }
625
+
626
+ case 'destroy': {
627
+ const entry = terminals.get(msg.sessionId);
628
+ if (entry) {
629
+ await _awaitQueueBounded(msg.sessionId);
630
+ entry.term.dispose();
631
+ terminals.delete(msg.sessionId);
632
+ writeQueues.delete(msg.sessionId);
633
+ }
634
+ break;
635
+ }
636
+ }
637
+ }
638
+
639
+ function dispatchHeadlessMessage(msg) {
640
+ const sessionId = msg && msg.sessionId;
641
+ if (!sessionId) {
642
+ handleMessage(msg).catch((err) => {
643
+ try {
644
+ postMessage({
645
+ type: 'worker-error',
646
+ sessionId: '',
647
+ message: err && err.message ? err.message : String(err),
648
+ });
649
+ } catch {}
650
+ });
651
+ return;
652
+ }
653
+ enqueueSessionOp(sessionId, () => handleMessage(msg));
654
+ }
655
+
656
+ function bindHeadlessTerminalService(options = {}) {
657
+ if (typeof options.postMessage !== 'function') {
658
+ throw new Error('bindHeadlessTerminalService requires postMessage');
659
+ }
660
+ postMessage = options.postMessage;
661
+ if (typeof options.onMessage === 'function') options.onMessage(dispatchHeadlessMessage);
662
+ return {
663
+ handleMessage: dispatchHeadlessMessage,
664
+ close() {
665
+ for (const [sessionId, entry] of terminals.entries()) {
666
+ try { entry.term.dispose(); } catch {}
667
+ terminals.delete(sessionId);
668
+ }
669
+ writeQueues.clear();
670
+ opQueues.clear();
671
+ },
672
+ };
673
+ }
674
+
675
+ module.exports = {
676
+ bindHeadlessTerminalService,
677
+ handleMessage: dispatchHeadlessMessage,
678
+ };