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,387 @@
1
+ 'use strict';
2
+
3
+ // Read/parse pool worker.
4
+ //
5
+ // One of N identical workers managed by lib/read-pool-client.js. Its job is to
6
+ // take the CPU/IO-heavy *read* work off the main event loop: parsing JSONL
7
+ // conversation transcripts (often 1–10MB `JSON.parse`) and batch `fs.stat`
8
+ // scans. These block the main thread for seconds and stall keystroke echo when
9
+ // many sessions are open (see lib/perf-tracker.js + the [perf-lag] log).
10
+ //
11
+ // Deliberately DB-free: the main thread resolves file paths via fast indexed
12
+ // SQLite lookups and passes them in as payload, so a pool worker never opens a
13
+ // SQLite connection. That keeps the pool simple (no read-only WAL connection to
14
+ // manage) and side-effect-free — it only reads files and returns plain data.
15
+ //
16
+ // Speaks the same message protocol as workers/db-owner-worker.js (ready /
17
+ // request / response / status / close) so it can share the client transport.
18
+
19
+ const { parentPort } = require('node:worker_threads');
20
+ const fs = require('node:fs');
21
+
22
+ let ready = false;
23
+ let closing = false;
24
+ let active = null;
25
+ let enqueued = 0;
26
+ let completed = 0;
27
+ let failed = 0;
28
+ const queue = [];
29
+
30
+ // Lazy so the worker boots instantly and a parser require error surfaces per
31
+ // request (as a normal op failure) instead of crashing the whole pool at boot.
32
+ let parser = null;
33
+ function _parser() {
34
+ if (!parser) parser = require('../lib/jsonl-conversation-parser');
35
+ return parser;
36
+ }
37
+
38
+ // Lazy require of the coding-agent model helpers (used by the resolveAgentModels op). DB-free
39
+ // except the Codex state DB it opens read-only — fine in a worker thread.
40
+ let _codingModels = null;
41
+ function _codingModelsMod() {
42
+ if (!_codingModels) _codingModels = require('../lib/coding-agent-models');
43
+ return _codingModels;
44
+ }
45
+
46
+ // Opportunity A follow-up: a READ-ONLY connection used only for the recent-sessions
47
+ // rebuild (getAllSessionsData — a ~700ms window-function query). Running it here keeps
48
+ // it off the main event loop. Read-only + lazy: opened on first use, cached per dbPath,
49
+ // never writes, never changes journal_mode (WAL readers don't need to). This is the one
50
+ // DB touch the otherwise DB-free pool makes, scoped to a pure read.
51
+ let _roDb = null;
52
+ let _roDbPath = null;
53
+ function _readonlyDb(dbPath) {
54
+ if (!dbPath) throw new Error('getAllSessionsData requires a dbPath');
55
+ if (_roDb && _roDbPath === dbPath) return _roDb;
56
+ if (_roDb) { try { _roDb.close(); } catch {} _roDb = null; _roDbPath = null; }
57
+ const { loadSqliteDriver } = require('../lib/sqlite-driver');
58
+ const Database = loadSqliteDriver({ source: 'read-pool-worker' });
59
+ const d = new Database(dbPath, { readonly: true, fileMustExist: true });
60
+ d.pragma('busy_timeout = 5000');
61
+ _roDb = d;
62
+ _roDbPath = dbPath;
63
+ return _roDb;
64
+ }
65
+
66
+ function _serializeError(err) {
67
+ return {
68
+ message: err && err.message ? err.message : String(err || 'unknown error'),
69
+ code: err && err.code ? err.code : undefined,
70
+ stack: err && err.stack ? String(err.stack) : undefined,
71
+ };
72
+ }
73
+
74
+ function _status() {
75
+ return { ready, active, running: !!active, pending: queue.length, enqueued, completed, failed };
76
+ }
77
+
78
+ function _postStatus() {
79
+ try { parentPort.postMessage({ type: 'status', status: _status() }); } catch {}
80
+ }
81
+
82
+ async function _runOp(op, payload = {}) {
83
+ switch (op) {
84
+ case 'ping': {
85
+ // Test hook: an optional bounded delay lets tests hold a worker busy to
86
+ // exercise pool concurrency/admission control deterministically.
87
+ const sleepMs = Math.min(5000, Math.max(0, Number(payload.sleepMs) || 0));
88
+ if (sleepMs > 0) await new Promise((r) => setTimeout(r, sleepMs));
89
+ return { pong: true, ts: Number(payload.ts) || 0 };
90
+ }
91
+
92
+ case 'statFiles': {
93
+ // Batch fs.stat off the main thread. apiSessionMessages and the scan jobs
94
+ // sum per-file sizes with synchronous statSync loops on the hot path.
95
+ const paths = Array.isArray(payload.paths) ? payload.paths : [];
96
+ let totalSize = 0;
97
+ const stats = paths.map((p) => {
98
+ try {
99
+ const st = fs.statSync(p);
100
+ totalSize += st.size;
101
+ return { path: p, exists: true, size: st.size, mtimeMs: st.mtimeMs };
102
+ } catch {
103
+ return { path: p, exists: false, size: 0, mtimeMs: 0 };
104
+ }
105
+ });
106
+ return { stats, totalSize };
107
+ }
108
+
109
+ case 'resolveAgentModels': {
110
+ // Resolve agent model ids (+ optional Codex titles) off the main loop. The claude path
111
+ // reads/scans the transcript JSONL (statSync + tail read + JSON.parse); the codex path
112
+ // opens the Codex state DB read-only — both blocked the main event loop in
113
+ // syncCodingSessionModels. Returns RAW model names; the main thread maps them to the model
114
+ // registry (cheap, no fs/DB) so the registry cache stays single-sourced on main.
115
+ const mod = _codingModelsMod();
116
+ const homeDir = payload.homeDir || process.env.HOME;
117
+ const items = Array.isArray(payload.items) ? payload.items : [];
118
+ const results = items.map((it) => {
119
+ const out = { id: it.id };
120
+ try {
121
+ if (it.agentType === 'claude') {
122
+ out.modelName = mod.getClaudeSessionModelName({
123
+ jsonlPath: it.claudeJsonlPath,
124
+ homeDir,
125
+ sessionActivityMs: it.sessionActivityMs || 0,
126
+ }) || null;
127
+ } else if (it.agentType === 'codex') {
128
+ out.modelName = it.codexThreadId
129
+ ? mod.getCodexThreadModel(it.codexThreadId, homeDir)
130
+ : mod.getCodexLatestModel(homeDir);
131
+ if (it.wantCodexTitle && it.codexThreadId) {
132
+ out.codexTitle = mod.getCodexThreadTitleById(it.codexThreadId, homeDir) || null;
133
+ }
134
+ }
135
+ } catch (e) {
136
+ out.error = e && e.message ? e.message : String(e);
137
+ }
138
+ return out;
139
+ });
140
+ return { results };
141
+ }
142
+
143
+ case 'parseSessionFiles': {
144
+ // Phase 2b consumer: read + parse a session's JSONL file set into the
145
+ // combined, chronologically-sorted message list the Conversation tab
146
+ // renders. The heavy fs.readFileSync + JSON.parse (up to 10MB) runs here,
147
+ // not on the main event loop. Returns the exact shape parseSessionFiles()
148
+ // returns on the main thread — parity is by construction (same function).
149
+ const files = Array.isArray(payload.files) ? payload.files : [];
150
+ return _parser().parseSessionFiles(files, payload.options || {});
151
+ }
152
+
153
+ case 'getAllSessionsData': {
154
+ // Opportunity A: run the recent-sessions rebuild query (~700ms) off the main
155
+ // event loop, on this worker's read-only connection. Same logic as
156
+ // db.getAllSessionsData() (shared lib/all-sessions-query). Returns plain rows.
157
+ const { queryAllSessionsData } = require('../lib/all-sessions-query');
158
+ return { rows: queryAllSessionsData(_readonlyDb(payload.dbPath)) };
159
+ }
160
+
161
+ case 'listPromptExecutions': {
162
+ // Move the /api/prompt-executions list query OFF the main event loop. It is
163
+ // `SELECT * FROM prompt_executions ... LIMIT ?` where LIMIT can be up to 10000
164
+ // and each row carries large message_text — a CPU profile of the live primary
165
+ // showed a single .all() blocking ~3.9s on the request path. Same SQL as the
166
+ // main-thread handler (shared lib → parity by construction); the heavy read
167
+ // runs here on this worker's read-only connection.
168
+ const { queryPromptExecutions } = require('../lib/prompt-executions-query');
169
+ return { executions: queryPromptExecutions(_readonlyDb(payload.dbPath), payload) };
170
+ }
171
+
172
+ case 'listRichWorktrees': {
173
+ // Move the worktree git fan-out OFF the main event loop. listRichWorktrees spawns
174
+ // ~4 git processes PER worktree (rev-list/status/log/unmerged); on a 37-worktree repo
175
+ // that's ~150 fork/execs whose synchronous spawn setup blocked the main loop for
176
+ // seconds every refresh (the dominant `(unknown)` freeze in the CPU profile). Running
177
+ // it here puts the spawn storm on a worker thread. Read-only fan-out only — the
178
+ // side-effecting autoRecover path stays on main.
179
+ const gitw = require('../git-utils');
180
+ const worktrees = await gitw.listRichWorktrees(payload.cwd, payload.opts || {});
181
+ return { worktrees: Array.isArray(worktrees) ? worktrees : [] };
182
+ }
183
+
184
+ case 'loadCursorConversation': {
185
+ // Phase 3: run the cursor blob-graph enumeration + reconstruction OFF the main event
186
+ // loop (the cold cache-miss path; steady state is served from session_conversations).
187
+ // Worker-safe: cursor-conversation-store uses only better-sqlite3 (readonly) + fs/path.
188
+ const cs = require('../lib/cursor-conversation-store');
189
+ const store = cs.loadCursorConversationForSession(payload.options || {});
190
+ if (!store || !Array.isArray(store.messages)) return { store: null };
191
+ return {
192
+ store: {
193
+ messages: store.messages,
194
+ agentId: store.agentId || '',
195
+ name: store.name || '',
196
+ workspacePath: store.workspacePath || '',
197
+ lastUsedModel: store.lastUsedModel || '',
198
+ createdAtMs: store.createdAtMs || 0,
199
+ storeDbPath: store.storeDbPath || '',
200
+ },
201
+ };
202
+ }
203
+
204
+ case 'parseConversationBlob': {
205
+ // Move the session_conversations cache-blob parse OFF the main event loop. The
206
+ // JSON.parse (0.5–2MB, deeply nested) + codex-synthetic scan + timeline sort is
207
+ // 150–360ms on main per the [freeze-probe]; growing/active sessions re-parse it on
208
+ // every poll. Same function as the main-thread fallback (shared module) → parity by
209
+ // construction. Clone-in (string) + clone-out (array) is ~3ms even at 1.7MB.
210
+ const { parseConversationBlob } = require('../lib/conversation-blob-parser');
211
+ return { messages: parseConversationBlob(payload.messages) };
212
+ }
213
+
214
+ case 'computeUserPromptIndex': {
215
+ // Phase 1: run the Queue Builder prompt-index compute (SELECT all user rows + sort
216
+ // + exclusions + sha1-per-prompt summary, ~460ms on a 328-prompt session) off the
217
+ // main event loop. Same logic as the main-thread fallback (shared module). Inputs
218
+ // (sourceIds, exclusion rows, codex-synthetic flag) are resolved on main.
219
+ const { computeUserPromptIndex } = require('../lib/session-prompt-index');
220
+ return computeUserPromptIndex(_readonlyDb(payload.dbPath), {
221
+ sourceIds: payload.sourceIds || [],
222
+ exclusionRows: payload.exclusionRows || [],
223
+ filterCodexSynthetic: payload.filterCodexSynthetic === true,
224
+ });
225
+ }
226
+
227
+ case 'autoTitleCandidates': {
228
+ // Run auto-title's candidate SELECT OFF the main event loop. The query
229
+ // (ORDER BY imported_at DESC LIMIT 20, LEFT JOIN ctm_sessions to find
230
+ // untitled ones) full-scans + sorts the session_conversations table whose
231
+ // rows carry large `messages` blobs — synchronously on the main loop it was
232
+ // measured freezing it up to ~25s on cold Dropbox pages every 5 min (the
233
+ // dominant auto-title freeze). Off-thread the cold cost is invisible to the
234
+ // loop. (An imported_at index was measured: it removes the sort but trades
235
+ // sequential scan for random row fetches that page in MORE on cold Dropbox —
236
+ // net slower cold, negligible warm — so we offload instead of indexing.)
237
+ // Read-only WAL connection → side-effect-free; same SQL as the main fallback.
238
+ const conn = _readonlyDb(payload.dbPath);
239
+ const rows = conn.prepare(`
240
+ SELECT sc.ctm_session_id as session_id, sc.first_message, sc.user_msg_count
241
+ FROM session_conversations sc
242
+ LEFT JOIN ctm_sessions cs ON sc.ctm_session_id = cs.id
243
+ WHERE (cs.title IS NULL OR cs.title = '')
244
+ AND sc.user_msg_count >= 2
245
+ AND sc.first_message != ''
246
+ ORDER BY sc.imported_at DESC
247
+ LIMIT 20
248
+ `).all();
249
+ return { rows };
250
+ }
251
+
252
+ case 'sessionIntegrityDetect': {
253
+ // Run session-integrity's READ-ONLY detection (the full-corpus mismatch scan +
254
+ // relink audit) OFF the db-owner worker. On cold Dropbox this scan takes ~60s;
255
+ // run on the single serial db-owner worker it MONOPOLIZES it, starving
256
+ // interactive writes onto the main thread where they stall on Dropbox I/O — the
257
+ // 30-min steady-state freeze. detectMismatches and auditRelink are both
258
+ // read-only, so they run safely on this worker's read-only connection. Only the
259
+ // rare recovery WRITES stay on the db-owner worker (applied by main when issues
260
+ // exist). Mirrors the read-only portions of session-integrity.runIntegrityCheck.
261
+ const roDb = _readonlyDb(payload.dbPath);
262
+ const integ = require('../session-integrity');
263
+ const { getAllSessionFiles } = require('../session-utils');
264
+ const issues = integ.detectMismatches(roDb, getAllSessionFiles);
265
+ let crossLinks = [];
266
+ try {
267
+ const { auditRelink } = require('../lib/relink-audit');
268
+ const audit = auditRelink({ db: roDb });
269
+ crossLinks = (audit.tabs || [])
270
+ .filter((t) => t.verdict === 'cross_linked')
271
+ .map((t) => ({
272
+ ctm_session_id: t.ctm_session_id,
273
+ label: t.label,
274
+ startup_target: t.startup_agent_id,
275
+ reason: t.details?.reason || 'cross_linked',
276
+ suggested_fix: t.details?.suggested_fix || null,
277
+ }));
278
+ } catch (e) {
279
+ // Relink audit is best-effort; detection issues are still returned.
280
+ }
281
+ return { issues, crossLinks };
282
+ }
283
+
284
+ case 'auditRelink': {
285
+ // Run the relink audit (full agent_sessions + session_conversations scan + a
286
+ // per-agent cold-JSONL header/first-message read) OFF the main event loop. It runs
287
+ // synchronously at the TOP of restore (repairSafeRelinkSwaps) where, on cold Dropbox,
288
+ // the N×M fs reads froze the loop for tens of seconds. auditRelink is fully read-only,
289
+ // so it runs on this worker's read-only connection; the rare repair WRITES stay on the
290
+ // main thread (repairSafeRelinkSwaps consumes this result). Returns the full audit.
291
+ const { auditRelink } = require('../lib/relink-audit');
292
+ return auditRelink({ db: _readonlyDb(payload.dbPath) });
293
+ }
294
+
295
+ case 'ctmSessionMemorySearch':
296
+ case 'ctmSessionMemoryContext':
297
+ case 'ctmSessionMemoryContextPack': {
298
+ // Run the Wall-E session-memory reads OFF the main event loop. These power the
299
+ // /api/ctm/session-memory/* endpoint that Wall-E MCP routing (walle_context_hint /
300
+ // lookup_context / context_pack / search_sessions) calls from EVERY active agent
301
+ // session. The work is brutal on the main thread: searchCtmSessions runs an FTS scan
302
+ // PLUS a `content LIKE '%q%'` scan over session_messages (220k+ rows) PLUS a
303
+ // `messages LIKE '%q%'` scan over the session_conversations blob column (tens of MB of
304
+ // JSON) PLUS a full JSON.parse of every matched conversation. A single search was
305
+ // measured blocking the CTM main loop 7.74s. Same shared wall-e module as the
306
+ // main-thread fallback → result parity by construction; the worker's read-only WAL
307
+ // connection makes it side-effect-free (better-sqlite3 readonly + fs only).
308
+ const ctx = require('../../wall-e/memory/ctm-session-context');
309
+ const args = (payload && payload.args) || {};
310
+ const conn = _readonlyDb(payload.dbPath);
311
+ if (op === 'ctmSessionMemorySearch') return ctx.searchCtmSessions({ ...args, db: conn });
312
+ if (op === 'ctmSessionMemoryContext') return ctx.getCtmSessionContext({ ...args, db: conn });
313
+ return ctx.buildContextPack({ ...args, db: conn });
314
+ }
315
+
316
+ case 'drain':
317
+ return { ok: true, ..._status() };
318
+
319
+ default: {
320
+ const err = new Error(`Unsupported read-pool op: ${op}`);
321
+ err.code = 'CTM_READ_POOL_UNSUPPORTED_OP';
322
+ throw err;
323
+ }
324
+ }
325
+ }
326
+
327
+ function _pump() {
328
+ if (active || closing) return;
329
+ const task = queue.shift();
330
+ if (!task) {
331
+ _postStatus();
332
+ return;
333
+ }
334
+ active = task.op;
335
+ _postStatus();
336
+ Promise.resolve()
337
+ .then(() => _runOp(task.op, task.payload))
338
+ .then((result) => {
339
+ completed += 1;
340
+ parentPort.postMessage({ type: 'response', requestId: task.requestId, result });
341
+ }, (err) => {
342
+ failed += 1;
343
+ parentPort.postMessage({ type: 'response', requestId: task.requestId, error: _serializeError(err) });
344
+ })
345
+ .finally(() => {
346
+ active = null;
347
+ _pump();
348
+ });
349
+ }
350
+
351
+ function _enqueue(msg) {
352
+ enqueued += 1;
353
+ queue.push({ requestId: msg.requestId, op: msg.op, payload: msg.payload || {} });
354
+ _pump();
355
+ }
356
+
357
+ function _close() {
358
+ closing = true;
359
+ queue.length = 0;
360
+ ready = false;
361
+ try { parentPort.postMessage({ type: 'closed' }); } catch {}
362
+ setImmediate(() => {
363
+ try { parentPort.close(); } catch {}
364
+ });
365
+ }
366
+
367
+ parentPort.on('message', (msg) => {
368
+ if (!msg || typeof msg !== 'object') return;
369
+ if (msg.type === 'request') { _enqueue(msg); return; }
370
+ if (msg.type === 'status') { _postStatus(); return; }
371
+ if (msg.type === 'close') {
372
+ if (active) {
373
+ const interval = setInterval(() => {
374
+ if (active) return;
375
+ clearInterval(interval);
376
+ _close();
377
+ }, 10);
378
+ if (typeof interval.unref === 'function') interval.unref();
379
+ } else {
380
+ _close();
381
+ }
382
+ }
383
+ });
384
+
385
+ // No DB to open — signal ready immediately.
386
+ ready = true;
387
+ try { parentPort.postMessage({ type: 'ready', status: _status() }); } catch {}
@@ -1,77 +1,16 @@
1
- // --- Scrollback DB Worker Thread ---
2
- // Handles SQLite scrollback writes off the main event loop.
3
- // Opens its own better-sqlite3 connection (WAL mode supports concurrent access).
1
+ 'use strict';
4
2
 
5
- const { parentPort, workerData } = require('worker_threads');
6
- const Database = require('better-sqlite3');
7
-
8
- const SCROLLBACK_MAX_CHUNKS = 5000;
9
-
10
- let db = null;
11
-
12
- function getDb() {
13
- if (db) return db;
14
- db = new Database(workerData.dbPath);
15
- db.pragma('journal_mode = WAL');
16
- db.pragma('busy_timeout = 5000');
17
- // Ensure scrollback_log table exists (may already exist from main thread)
18
- db.prepare(`CREATE TABLE IF NOT EXISTS scrollback_log (
19
- ctm_session_id TEXT NOT NULL,
20
- chunk_seq INTEGER NOT NULL,
21
- data TEXT NOT NULL,
22
- PRIMARY KEY (ctm_session_id, chunk_seq)
23
- )`).run();
24
- const cols = db.prepare('PRAGMA table_info(scrollback_log)').all().map(c => c.name);
25
- if (cols.includes('session_id') && !cols.includes('ctm_session_id')) {
26
- db.exec('ALTER TABLE scrollback_log RENAME COLUMN session_id TO ctm_session_id');
27
- }
28
- return db;
29
- }
3
+ const { parentPort } = require('node:worker_threads');
30
4
 
31
5
  parentPort.on('message', (msg) => {
32
- switch (msg.type) {
33
- case 'append': {
34
- try {
35
- const d = getDb();
36
- const insert = d.prepare('INSERT OR REPLACE INTO scrollback_log (ctm_session_id, chunk_seq, data) VALUES (?, ?, ?)');
37
- const txn = d.transaction((items) => {
38
- for (const c of items) insert.run(msg.sessionId, c.seq, c.data);
39
- const maxSeq = items[items.length - 1]?.seq || 0;
40
- if (maxSeq > SCROLLBACK_MAX_CHUNKS) {
41
- const cutoff = maxSeq - SCROLLBACK_MAX_CHUNKS;
42
- d.prepare('DELETE FROM scrollback_log WHERE ctm_session_id = ? AND chunk_seq < ?').run(msg.sessionId, cutoff);
43
- }
44
- });
45
- txn(msg.chunks);
46
- if (msg.requestId) parentPort.postMessage({ type: 'appended', requestId: msg.requestId });
47
- } catch (e) {
48
- // Non-fatal — scrollback is best-effort
49
- parentPort.postMessage({ type: 'error', op: 'append', error: e.message, requestId: msg.requestId });
50
- }
51
- break;
52
- }
53
- case 'clear': {
54
- try {
55
- getDb().prepare('DELETE FROM scrollback_log WHERE ctm_session_id = ?').run(msg.sessionId);
56
- if (msg.requestId) parentPort.postMessage({ type: 'cleared', requestId: msg.requestId });
57
- } catch (e) {
58
- parentPort.postMessage({ type: 'error', op: 'clear', error: e.message, requestId: msg.requestId });
59
- }
60
- break;
61
- }
62
- case 'close': {
63
- // Gracefully close DB connection before terminate() kills the thread.
64
- // This ensures any in-flight WAL writes are completed and the connection
65
- // is properly closed, preventing WAL corruption during shutdown.
66
- if (db) { try { db.close(); } catch {} db = null; }
67
- parentPort.postMessage({ type: 'closed' });
68
- setImmediate(() => process.exit(0));
69
- break;
70
- }
6
+ if (msg?.type === 'close') {
7
+ parentPort.postMessage({ type: 'closed' });
8
+ return;
71
9
  }
72
- });
73
-
74
- // Cleanup on thread exit
75
- process.on('exit', () => {
76
- if (db) { try { db.close(); } catch {} }
10
+ parentPort.postMessage({
11
+ type: 'error',
12
+ op: msg?.type || 'unknown',
13
+ requestId: msg?.requestId,
14
+ error: 'scrollback DB writes are owned by the CTM DB owner worker',
15
+ });
77
16
  });
@@ -0,0 +1,146 @@
1
+ 'use strict';
2
+
3
+ // Per-session PTY host process. This owns node-pty for exactly one CTM
4
+ // terminal session and embeds the shared headless terminal service for the same
5
+ // session. The primary CTM process stays as the control plane; this process owns
6
+ // the native PTY resource so node-pty never crosses worker_threads boundaries.
7
+
8
+ process.env.CTM_PROCESS_ROLE = process.env.CTM_PROCESS_ROLE || 'ctm-session-host';
9
+
10
+ const pty = require('node-pty');
11
+ const { bindHeadlessTerminalService } = require('../lib/headless-term-service');
12
+
13
+ let sessionId = '';
14
+ let ptyProcess = null;
15
+ let ptyClosed = false;
16
+ let postExitShutdownTimer = null;
17
+ const POST_EXIT_HEADLESS_TTL_MS = 60000;
18
+
19
+ function send(msg) {
20
+ if (typeof process.send !== 'function') return;
21
+ try { process.send(msg); } catch {}
22
+ }
23
+
24
+ const headless = bindHeadlessTerminalService({
25
+ postMessage: (message) => send({ type: 'headless-message', sessionId: message.sessionId || sessionId, message }),
26
+ });
27
+
28
+ function hostError(message, details = {}) {
29
+ send({
30
+ type: 'host-error',
31
+ sessionId,
32
+ message: message && message.message ? message.message : String(message || 'session host error'),
33
+ ...details,
34
+ });
35
+ }
36
+
37
+ function schedulePostExitShutdown() {
38
+ if (postExitShutdownTimer) return;
39
+ postExitShutdownTimer = setTimeout(() => {
40
+ headless.close();
41
+ process.exit(0);
42
+ }, POST_EXIT_HEADLESS_TTL_MS);
43
+ postExitShutdownTimer.unref?.();
44
+ }
45
+
46
+ function finishHostProcess() {
47
+ if (postExitShutdownTimer) {
48
+ clearTimeout(postExitShutdownTimer);
49
+ postExitShutdownTimer = null;
50
+ }
51
+ headless.close();
52
+ setTimeout(() => process.exit(0), 0);
53
+ }
54
+
55
+ function handleHeadlessMessage(message) {
56
+ const payload = message || {};
57
+ try {
58
+ headless.handleMessage(payload);
59
+ } catch (err) {
60
+ hostError(err, { code: 'headless_message_failed' });
61
+ }
62
+ if (ptyClosed && payload.type === 'destroy') {
63
+ finishHostProcess();
64
+ }
65
+ }
66
+
67
+ function startSession(msg) {
68
+ if (ptyProcess) return;
69
+ sessionId = String(msg.sessionId || '');
70
+ const cols = Number(msg.cols || 120) || 120;
71
+ const rows = Number(msg.rows || 30) || 30;
72
+ try {
73
+ headless.handleMessage({ type: 'create', sessionId, cols, rows });
74
+ ptyProcess = pty.spawn(msg.cmd, Array.isArray(msg.args) ? msg.args : [], {
75
+ name: msg.name || 'xterm-256color',
76
+ cols,
77
+ rows,
78
+ cwd: msg.cwd || process.cwd(),
79
+ env: msg.env || process.env,
80
+ });
81
+ send({ type: 'started', sessionId, pid: ptyProcess.pid, cols, rows });
82
+ ptyProcess.onData((data) => {
83
+ const text = String(data || '');
84
+ Promise.resolve(headless.handleMessage({ type: 'feed', sessionId, data: text }))
85
+ .catch((err) => hostError(err, { code: 'headless_feed_failed' }));
86
+ send({ type: 'data', sessionId, data: text });
87
+ });
88
+ ptyProcess.onExit(({ exitCode, signal }) => {
89
+ ptyClosed = true;
90
+ send({ type: 'exit', sessionId, exitCode, signal });
91
+ // Keep the embedded headless terminal alive after PTY exit. The parent
92
+ // process still needs one final serialize pass to persist/restore the
93
+ // complete terminal screen before it sends the destroy message.
94
+ schedulePostExitShutdown();
95
+ });
96
+ } catch (err) {
97
+ hostError(err, { code: 'spawn_failed' });
98
+ send({ type: 'exit', sessionId, exitCode: 1, signal: null, spawnFailed: true });
99
+ headless.close();
100
+ setTimeout(() => process.exit(1), 0);
101
+ }
102
+ }
103
+
104
+ function handleMessage(msg) {
105
+ if (!msg || typeof msg !== 'object') return;
106
+ switch (msg.type) {
107
+ case 'start':
108
+ startSession(msg);
109
+ break;
110
+ case 'input':
111
+ if (ptyProcess && !ptyClosed) ptyProcess.write(String(msg.data || ''));
112
+ break;
113
+ case 'resize': {
114
+ if (ptyProcess && !ptyClosed) {
115
+ const cols = Number(msg.cols || 0);
116
+ const rows = Number(msg.rows || 0);
117
+ if (cols > 0 && rows > 0) ptyProcess.resize(cols, rows);
118
+ }
119
+ break;
120
+ }
121
+ case 'kill':
122
+ if (ptyProcess && !ptyClosed) ptyProcess.kill();
123
+ else finishHostProcess();
124
+ break;
125
+ case 'headless':
126
+ handleHeadlessMessage(msg.message || {});
127
+ break;
128
+ case 'shutdown':
129
+ try { if (ptyProcess && !ptyClosed) ptyProcess.kill(); } catch {}
130
+ finishHostProcess();
131
+ break;
132
+ }
133
+ }
134
+
135
+ process.on('message', handleMessage);
136
+ process.on('disconnect', finishHostProcess);
137
+ process.on('uncaughtException', (err) => {
138
+ hostError(err, { code: 'uncaught_exception' });
139
+ process.exit(1);
140
+ });
141
+ process.on('unhandledRejection', (err) => {
142
+ hostError(err, { code: 'unhandled_rejection' });
143
+ process.exit(1);
144
+ });
145
+
146
+ send({ type: 'ready' });