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,444 @@
1
+ 'use strict';
2
+
3
+ const ACTIVE_STATUSES = new Set(['queued', 'running', 'stopping']);
4
+ const TERMINAL_STATUSES = new Set(['completed', 'failed', 'cancelled', 'stopped', 'blocked', 'no_change', 'read_only']);
5
+ const RECENT_COMPLETED_MS = 5 * 60 * 1000;
6
+ const MAX_SNAPSHOT_TASKS = 80;
7
+ const ACTIVE_TASK_STALE_MS = Math.max(10 * 60 * 1000, Number(process.env.CTM_RUNTIME_WORK_ACTIVE_STALE_MS) || 2 * 60 * 60 * 1000);
8
+
9
+ function nowDefault() {
10
+ return Date.now();
11
+ }
12
+
13
+ function cleanText(value, max = 240) {
14
+ return String(value == null ? '' : value)
15
+ .replace(/\s+/g, ' ')
16
+ .trim()
17
+ .slice(0, max);
18
+ }
19
+
20
+ function stableJsonPreview(value, max = 500) {
21
+ if (value == null || value === '') return '';
22
+ if (typeof value === 'string') return cleanText(value, max);
23
+ try {
24
+ return cleanText(JSON.stringify(value), max);
25
+ } catch {
26
+ return cleanText(String(value), max);
27
+ }
28
+ }
29
+
30
+ function toolEventName(event = {}) {
31
+ return cleanText(event.tool || event.name || event.toolName || event.tool_name || event.skill || 'tool', 80) || 'tool';
32
+ }
33
+
34
+ function toolEventId(event = {}, name = '') {
35
+ return cleanText(event.id || event.toolCallId || event.tool_call_id || event.callId || event.call_id || name || 'tool', 120);
36
+ }
37
+
38
+ function kindForTool(name) {
39
+ const clean = String(name || '').toLowerCase();
40
+ if (/shell|bash|command|terminal|exec/.test(clean)) return 'shell';
41
+ if (/skill/.test(clean)) return 'skill';
42
+ if (/agent|subagent|worker/.test(clean)) return 'agent';
43
+ return 'tool';
44
+ }
45
+
46
+ function taskPublicShape(task) {
47
+ return {
48
+ taskId: task.taskId,
49
+ ctmSessionId: task.ctmSessionId,
50
+ agentSessionId: task.agentSessionId || '',
51
+ turnId: task.turnId || '',
52
+ kind: task.kind || 'task',
53
+ label: task.label || '',
54
+ status: task.status || 'running',
55
+ foregroundState: task.foregroundState || 'foreground',
56
+ provider: task.provider || '',
57
+ model: task.model || '',
58
+ toolName: task.toolName || '',
59
+ summary: task.summary || '',
60
+ inputPreview: task.inputPreview || '',
61
+ outputRef: task.outputRef || '',
62
+ controlCaps: Array.isArray(task.controlCaps) ? task.controlCaps.slice(0, 8) : [],
63
+ workerScope: task.workerScope || '',
64
+ startedAt: Number(task.startedAt || 0),
65
+ updatedAt: Number(task.updatedAt || 0),
66
+ completedAt: Number(task.completedAt || 0),
67
+ metadata: task.metadata && typeof task.metadata === 'object' ? task.metadata : {},
68
+ };
69
+ }
70
+
71
+ function normalizeDbTask(row) {
72
+ if (!row) return null;
73
+ return {
74
+ taskId: row.taskId || row.task_id,
75
+ ctmSessionId: row.ctmSessionId || row.ctm_session_id,
76
+ agentSessionId: row.agentSessionId || row.agent_session_id || '',
77
+ turnId: row.turnId || row.turn_id || '',
78
+ kind: row.kind || 'task',
79
+ label: row.label || '',
80
+ status: row.status || 'running',
81
+ foregroundState: row.foregroundState || row.foreground_state || 'foreground',
82
+ provider: row.provider || '',
83
+ model: row.model || '',
84
+ toolName: row.toolName || row.tool_name || '',
85
+ summary: row.summary || '',
86
+ inputPreview: row.inputPreview || row.input_preview || '',
87
+ outputRef: row.outputRef || row.output_ref || '',
88
+ controlCaps: Array.isArray(row.controlCaps) ? row.controlCaps : (Array.isArray(row.control_caps) ? row.control_caps : []),
89
+ workerScope: row.workerScope || row.worker_scope || '',
90
+ startedAt: Number(row.startedAt || row.started_at || 0),
91
+ updatedAt: Number(row.updatedAt || row.updated_at || 0),
92
+ completedAt: Number(row.completedAt || row.completed_at || 0),
93
+ metadata: row.metadata && typeof row.metadata === 'object' ? row.metadata : {},
94
+ };
95
+ }
96
+
97
+ function publicTaskWithStaleActiveGuard(task, now = Date.now()) {
98
+ if (!task || !ACTIVE_STATUSES.has(task.status)) return taskPublicShape(task);
99
+ const updatedAt = Number(task.updatedAt || task.startedAt || 0) || 0;
100
+ if (!updatedAt || (now - updatedAt) <= ACTIVE_TASK_STALE_MS) return taskPublicShape(task);
101
+ return taskPublicShape({
102
+ ...task,
103
+ status: 'stopped',
104
+ foregroundState: 'background',
105
+ completedAt: updatedAt,
106
+ summary: task.summary || 'Stale running marker; terminal state is authoritative.',
107
+ metadata: {
108
+ ...(task.metadata || {}),
109
+ staleActiveMarker: true,
110
+ staleAfterMs: ACTIVE_TASK_STALE_MS,
111
+ },
112
+ });
113
+ }
114
+
115
+ class RuntimeWorkRegistry {
116
+ constructor({ db, logger = console, now = nowDefault } = {}) {
117
+ this.db = db || null;
118
+ this.logger = logger;
119
+ this.now = now;
120
+ this.sessions = new Map();
121
+ this.lastPersistErrorAt = 0;
122
+ }
123
+
124
+ _sessionMap(sessionId) {
125
+ const key = String(sessionId || '').trim();
126
+ if (!key) return null;
127
+ let map = this.sessions.get(key);
128
+ if (!map) {
129
+ map = new Map();
130
+ this.sessions.set(key, map);
131
+ this._hydrateSession(key, map);
132
+ }
133
+ return map;
134
+ }
135
+
136
+ _hydrateSession(sessionId, map) {
137
+ if (!this.db || typeof this.db.listRuntimeTasks !== 'function') return;
138
+ try {
139
+ const rows = this.db.listRuntimeTasks(sessionId, {
140
+ includeCompletedSince: this.now() - RECENT_COMPLETED_MS,
141
+ limit: MAX_SNAPSHOT_TASKS,
142
+ });
143
+ for (const row of rows || []) {
144
+ const task = normalizeDbTask(row);
145
+ if (task?.taskId) map.set(task.taskId, task);
146
+ }
147
+ } catch (err) {
148
+ this._logPersistError('hydrate', err);
149
+ }
150
+ }
151
+
152
+ _persist(task) {
153
+ if (!this.db || typeof this.db.upsertRuntimeTask !== 'function') return;
154
+ try {
155
+ this.db.upsertRuntimeTask({
156
+ taskId: task.taskId,
157
+ ctmSessionId: task.ctmSessionId,
158
+ agentSessionId: task.agentSessionId,
159
+ turnId: task.turnId,
160
+ kind: task.kind,
161
+ label: task.label,
162
+ status: task.status,
163
+ foregroundState: task.foregroundState,
164
+ provider: task.provider,
165
+ model: task.model,
166
+ toolName: task.toolName,
167
+ summary: task.summary,
168
+ inputPreview: task.inputPreview,
169
+ outputRef: task.outputRef,
170
+ controlCaps: task.controlCaps,
171
+ workerScope: task.workerScope,
172
+ startedAt: task.startedAt,
173
+ updatedAt: task.updatedAt,
174
+ completedAt: task.completedAt,
175
+ metadata: task.metadata,
176
+ });
177
+ } catch (err) {
178
+ this._logPersistError('persist', err);
179
+ }
180
+ }
181
+
182
+ _logPersistError(action, err) {
183
+ const now = this.now();
184
+ if (now - this.lastPersistErrorAt < 30000) return;
185
+ this.lastPersistErrorAt = now;
186
+ try {
187
+ this.logger.warn?.(`[runtime-work] ${action} failed: ${err?.message || err}`);
188
+ } catch {}
189
+ }
190
+
191
+ _upsert(sessionId, task) {
192
+ const map = this._sessionMap(sessionId);
193
+ if (!map || !task?.taskId) return null;
194
+ const existing = map.get(task.taskId) || {};
195
+ const now = this.now();
196
+ const merged = {
197
+ ...existing,
198
+ ...task,
199
+ ctmSessionId: sessionId,
200
+ startedAt: Number(existing.startedAt || task.startedAt || now) || now,
201
+ updatedAt: Number(task.updatedAt || now) || now,
202
+ controlCaps: Array.isArray(task.controlCaps)
203
+ ? task.controlCaps
204
+ : (Array.isArray(existing.controlCaps) ? existing.controlCaps : []),
205
+ metadata: {
206
+ ...(existing.metadata || {}),
207
+ ...(task.metadata || {}),
208
+ },
209
+ };
210
+ if (TERMINAL_STATUSES.has(merged.status) && !merged.completedAt) merged.completedAt = now;
211
+ if (ACTIVE_STATUSES.has(merged.status)) merged.completedAt = 0;
212
+ map.set(merged.taskId, merged);
213
+ this._persist(merged);
214
+ return taskPublicShape(merged);
215
+ }
216
+
217
+ startTurn({ sessionId, agentSessionId = '', turnId = '', prompt = '', provider = '', model = '', cwd = '', workerScope = 'walle-runtime' } = {}) {
218
+ const cleanSessionId = String(sessionId || '').trim();
219
+ if (!cleanSessionId) return null;
220
+ const cleanTurnId = cleanText(turnId || `turn-${this.now()}`, 160);
221
+ return this._upsert(cleanSessionId, {
222
+ taskId: `${cleanSessionId}:turn:${cleanTurnId}`,
223
+ agentSessionId,
224
+ turnId: cleanTurnId,
225
+ kind: 'turn',
226
+ label: cleanText(prompt, 120) || 'Wall-E turn',
227
+ status: 'running',
228
+ foregroundState: 'foreground',
229
+ provider,
230
+ model,
231
+ summary: 'Thinking',
232
+ inputPreview: cleanText(prompt, 500),
233
+ controlCaps: ['view', 'stop'],
234
+ workerScope,
235
+ metadata: { cwd },
236
+ });
237
+ }
238
+
239
+ applyWalleProgress({ sessionId, agentSessionId = '', turnId = '', event = {}, provider = '', model = '', workerScope = 'walle-runtime' } = {}) {
240
+ const cleanSessionId = String(sessionId || '').trim();
241
+ const cleanTurnId = cleanText(turnId, 160);
242
+ if (!cleanSessionId || !event || typeof event !== 'object') return null;
243
+ const type = String(event.type || '').toLowerCase();
244
+ if (type === 'thinking') {
245
+ return this._upsert(cleanSessionId, {
246
+ taskId: `${cleanSessionId}:turn:${cleanTurnId || 'current'}`,
247
+ agentSessionId,
248
+ turnId: cleanTurnId,
249
+ kind: 'turn',
250
+ label: 'Wall-E turn',
251
+ status: 'running',
252
+ provider,
253
+ model,
254
+ summary: 'Thinking',
255
+ controlCaps: ['view', 'stop'],
256
+ workerScope,
257
+ });
258
+ }
259
+ if (type === 'tool_call') {
260
+ const name = toolEventName(event);
261
+ const eventId = toolEventId(event, name);
262
+ return this._upsert(cleanSessionId, {
263
+ taskId: `${cleanSessionId}:turn:${cleanTurnId || 'current'}:${eventId}`,
264
+ agentSessionId,
265
+ turnId: cleanTurnId,
266
+ kind: kindForTool(name),
267
+ label: cleanText(event.summary || name, 140) || name,
268
+ status: 'running',
269
+ foregroundState: 'foreground',
270
+ provider,
271
+ model,
272
+ toolName: name,
273
+ summary: cleanText(event.summary || `Running ${name}`, 240),
274
+ inputPreview: stableJsonPreview(event.input || event.args || event.parameters || '', 500),
275
+ controlCaps: ['view'],
276
+ workerScope,
277
+ metadata: { eventId },
278
+ });
279
+ }
280
+ if (type === 'tool_result' || type === 'tool_done') {
281
+ const explicitName = event.tool || event.name || event.toolName || event.tool_name || '';
282
+ const name = explicitName ? toolEventName(event) : '';
283
+ const eventId = toolEventId(event, name);
284
+ const taskId = `${cleanSessionId}:turn:${cleanTurnId || 'current'}:${eventId}`;
285
+ const existing = this._sessionMap(cleanSessionId)?.get(taskId) || null;
286
+ const resolvedName = name || existing?.toolName || 'tool';
287
+ const failed = !!event.error || !!event.failed || String(event.status || '').toLowerCase() === 'error';
288
+ return this._upsert(cleanSessionId, {
289
+ taskId,
290
+ agentSessionId,
291
+ turnId: cleanTurnId,
292
+ kind: existing?.kind || kindForTool(resolvedName),
293
+ label: existing?.label || cleanText(event.summary || resolvedName, 140) || resolvedName,
294
+ status: failed ? 'failed' : 'completed',
295
+ foregroundState: 'background',
296
+ provider,
297
+ model,
298
+ toolName: resolvedName,
299
+ summary: cleanText(event.summary || (failed ? `Failed ${resolvedName}` : `Completed ${resolvedName}`), 240),
300
+ inputPreview: stableJsonPreview(event.input || event.args || '', 500),
301
+ outputRef: cleanText(event.outputRef || event.output_ref || '', 500),
302
+ controlCaps: ['view'],
303
+ workerScope,
304
+ metadata: {
305
+ eventId,
306
+ error: cleanText(event.error || '', 300),
307
+ },
308
+ });
309
+ }
310
+ if (type === 'skill_loaded' || type === 'skill_load_failed') {
311
+ const name = cleanText(event.skill || event.name || 'skill', 80);
312
+ const failed = type === 'skill_load_failed';
313
+ const eventId = toolEventId(event, `skill-${name}`);
314
+ return this._upsert(cleanSessionId, {
315
+ taskId: `${cleanSessionId}:turn:${cleanTurnId || 'current'}:${eventId}`,
316
+ agentSessionId,
317
+ turnId: cleanTurnId,
318
+ kind: 'skill',
319
+ label: failed ? `Skill failed: ${name}` : `Skill loaded: ${name}`,
320
+ status: failed ? 'failed' : 'completed',
321
+ foregroundState: 'background',
322
+ provider,
323
+ model,
324
+ toolName: 'skill_loaded',
325
+ summary: cleanText(event.summary || (failed ? `Failed to load ${name}` : `Loaded ${name}`), 240),
326
+ inputPreview: stableJsonPreview(event.input || event.args || { skill_name: name }, 500),
327
+ controlCaps: ['view'],
328
+ workerScope,
329
+ metadata: { eventId, skill: name },
330
+ });
331
+ }
332
+ if (type === 'cancelled') {
333
+ return this.completeTurn({ sessionId: cleanSessionId, turnId: cleanTurnId, status: 'cancelled', summary: 'Cancelled' });
334
+ }
335
+ return null;
336
+ }
337
+
338
+ completeTurn({ sessionId, turnId = '', status = 'completed', summary = '' } = {}) {
339
+ const cleanSessionId = String(sessionId || '').trim();
340
+ const cleanTurnId = cleanText(turnId, 160);
341
+ if (!cleanSessionId) return null;
342
+ const map = this._sessionMap(cleanSessionId);
343
+ if (!map) return null;
344
+ const normalizedStatus = TERMINAL_STATUSES.has(status) ? status : 'completed';
345
+ const now = this.now();
346
+ let changed = null;
347
+ for (const task of map.values()) {
348
+ if (cleanTurnId && task.turnId !== cleanTurnId) continue;
349
+ if (!ACTIVE_STATUSES.has(task.status)) continue;
350
+ changed = this._upsert(cleanSessionId, {
351
+ ...task,
352
+ status: normalizedStatus,
353
+ foregroundState: 'background',
354
+ summary: cleanText(summary || task.summary || normalizedStatus, 240),
355
+ updatedAt: now,
356
+ completedAt: now,
357
+ });
358
+ }
359
+ return changed;
360
+ }
361
+
362
+ requestStop(sessionId) {
363
+ const cleanSessionId = String(sessionId || '').trim();
364
+ const map = this._sessionMap(cleanSessionId);
365
+ if (!map) return null;
366
+ let changed = null;
367
+ for (const task of map.values()) {
368
+ if (!ACTIVE_STATUSES.has(task.status)) continue;
369
+ changed = this._upsert(cleanSessionId, {
370
+ ...task,
371
+ status: 'stopping',
372
+ summary: 'Stopping',
373
+ updatedAt: this.now(),
374
+ });
375
+ }
376
+ return changed;
377
+ }
378
+
379
+ snapshot(sessionId, { queue = null } = {}) {
380
+ const cleanSessionId = String(sessionId || '').trim();
381
+ const map = this._sessionMap(cleanSessionId) || new Map();
382
+ const now = this.now();
383
+ const tasks = Array.from(map.values())
384
+ .filter((task) => ACTIVE_STATUSES.has(task.status) || !task.completedAt || now - task.completedAt <= RECENT_COMPLETED_MS)
385
+ .sort((a, b) => {
386
+ const activeDelta = (ACTIVE_STATUSES.has(b.status) ? 1 : 0) - (ACTIVE_STATUSES.has(a.status) ? 1 : 0);
387
+ if (activeDelta) return activeDelta;
388
+ return Number(b.updatedAt || 0) - Number(a.updatedAt || 0);
389
+ })
390
+ .slice(0, MAX_SNAPSHOT_TASKS)
391
+ .map(task => publicTaskWithStaleActiveGuard(task, now));
392
+ const counts = tasks.reduce((acc, task) => {
393
+ if (ACTIVE_STATUSES.has(task.status)) acc.running += 1;
394
+ if (task.kind === 'shell' && ACTIVE_STATUSES.has(task.status)) acc.shells += 1;
395
+ if (task.kind === 'agent' && ACTIVE_STATUSES.has(task.status)) acc.agents += 1;
396
+ if (task.kind === 'tool' && ACTIVE_STATUSES.has(task.status)) acc.tools += 1;
397
+ if (task.kind === 'skill' && ACTIVE_STATUSES.has(task.status)) acc.skills += 1;
398
+ return acc;
399
+ }, { running: 0, shells: 0, agents: 0, tools: 0, skills: 0, queued: 0 });
400
+ if (queue && Array.isArray(queue.items)) {
401
+ counts.queued = queue.items.filter((item) => {
402
+ const status = String(item?.status || 'pending').toLowerCase();
403
+ return status !== 'sent' && status !== 'skipped';
404
+ }).length;
405
+ }
406
+ return {
407
+ sessionId: cleanSessionId,
408
+ updatedAt: now,
409
+ tasks,
410
+ active: tasks.filter((task) => ACTIVE_STATUSES.has(task.status)),
411
+ recent: tasks.filter((task) => !ACTIVE_STATUSES.has(task.status)),
412
+ counts,
413
+ isolation: {
414
+ owner: 'ctm-runtime-work-registry',
415
+ outputPolicy: 'metadata-only',
416
+ },
417
+ };
418
+ }
419
+
420
+ summary(sessionId) {
421
+ const snapshot = this.snapshot(sessionId);
422
+ return {
423
+ running: snapshot.counts.running,
424
+ shells: snapshot.counts.shells,
425
+ agents: snapshot.counts.agents,
426
+ tools: snapshot.counts.tools,
427
+ skills: snapshot.counts.skills,
428
+ queued: snapshot.counts.queued,
429
+ updatedAt: snapshot.updatedAt,
430
+ };
431
+ }
432
+ }
433
+
434
+ function createRuntimeWorkRegistry(options) {
435
+ return new RuntimeWorkRegistry(options);
436
+ }
437
+
438
+ module.exports = {
439
+ ACTIVE_STATUSES,
440
+ TERMINAL_STATUSES,
441
+ RuntimeWorkRegistry,
442
+ createRuntimeWorkRegistry,
443
+ kindForTool,
444
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+
5
+ function isCodexCommand(cmd) {
6
+ const first = String(cmd || '').trim().split(/\s+/)[0] || '';
7
+ return path.basename(first).toLowerCase() === 'codex';
8
+ }
9
+
10
+ /**
11
+ * Strip env-injected OpenAI auth before spawning an interactive Codex CLI.
12
+ *
13
+ * CTM itself may have OPENAI_API_KEY/OPENAI_BASE_URL in its process env for
14
+ * provider setup and background jobs. Interactive Codex sessions should use
15
+ * Codex's own login/config state unless the operator explicitly opts into env
16
+ * inheritance; otherwise a stale API-key route can override the user's normal
17
+ * ChatGPT/Codex auth path and select a different transport/billing mode.
18
+ */
19
+ function sanitizeOpenAiAuth(env, cmd) {
20
+ if (!env || typeof env !== 'object') return env;
21
+ if (!isCodexCommand(cmd)) return env;
22
+ if (String(process.env.CTM_KEEP_OPENAI_ENV_FOR_CODEX || '').trim()) return env;
23
+ delete env.OPENAI_API_KEY;
24
+ delete env.OPENAI_BASE_URL;
25
+ return env;
26
+ }
27
+
28
+ module.exports = {
29
+ isCodexCommand,
30
+ sanitizeOpenAiAuth,
31
+ };
@@ -304,17 +304,37 @@ class Scheduler extends EventEmitter {
304
304
  // 3. Sort by priority (lower = higher priority)
305
305
  candidates.sort((a, b) => a.descriptor.priority - b.descriptor.priority);
306
306
 
307
- // 4. Dispatch respecting pool limits
307
+ // 4. Dispatch respecting pool limits + a per-tick cap on heavy jobs.
308
+ // Jobs flagged `heavy: true` each run a long synchronous DB/fs/parse span
309
+ // that blocks the event loop. Even within the io pool's concurrency limit,
310
+ // letting several start on the same tick stacks those spans back-to-back and
311
+ // produces the multi-second stalls. Cap heavy dispatch to ONE per tick
312
+ // (others queue and are reconsidered next tick, 5s later) so their blocking
313
+ // spans spread out instead of piling up. Non-heavy jobs are unaffected.
314
+ let heavyDispatchedThisTick = this._countActiveHeavyJobs();
308
315
  for (const job of candidates) {
309
316
  const pool = job.descriptor.pool;
310
317
  if (pool && !this._poolHasCapacity(pool)) {
311
318
  job.state.status = 'queued';
312
319
  continue;
313
320
  }
321
+ if (job.descriptor.heavy && heavyDispatchedThisTick >= 1) {
322
+ job.state.status = 'queued';
323
+ continue;
324
+ }
325
+ if (job.descriptor.heavy) heavyDispatchedThisTick++;
314
326
  this._dispatch(job);
315
327
  }
316
328
  }
317
329
 
330
+ _countActiveHeavyJobs() {
331
+ let n = 0;
332
+ for (const [, job] of this._jobs) {
333
+ if (job.descriptor.heavy && job.state.status === 'running') n++;
334
+ }
335
+ return n;
336
+ }
337
+
318
338
  _poolHasCapacity(pool) {
319
339
  const active = this._poolActive.get(pool);
320
340
  const limit = this._poolLimits[pool];
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const fsp = fs.promises;
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ // Transient per-session scrollback snapshots stored as LOCAL files — a
9
+ // "hibernation" hand-off, not a durable store.
10
+ //
11
+ // Scrollback is purely a visual terminal-repaint cache: the agent resumes
12
+ // independently via `--resume`; scrollback is only replayed to the browser xterm
13
+ // after a CTM *server* restart. So instead of streaming every chunk into the
14
+ // shared SQLite write lock (~1 write/sec/active-session — the last high-frequency
15
+ // per-session DB writer and a structural bottleneck at scale), we serialize each
16
+ // ACTIVE session's headless-xterm buffer to one file (full overwrite, not append)
17
+ // on graceful shutdown + periodically as crash insurance, repaint-and-delete it on
18
+ // startup, and delete it when the session closes. Files exist only for active
19
+ // sessions, so there is no retention/TTL problem to manage.
20
+ //
21
+ // Files live on LOCAL disk (default ~/.walle/cache/scrollback), NEVER under
22
+ // CTM_DATA_DIR — on the primary that is the Dropbox dir, and syncing transient
23
+ // repaint blobs is exactly the churn this change exists to avoid.
24
+ //
25
+ // Format: one JSON file per session, `<sessionId>.scrollback.json`, holding the
26
+ // serialized blob plus a small identity header (agentSessionId/cols/rows/fp/ts) so
27
+ // restore can validate the snapshot belongs to the session being restored.
28
+ //
29
+ // LOCATION: under the CTM data dir (`$CTM_DATA_DIR/scrollback/`, default
30
+ // `~/.walle/data/scrollback`). The data dir is already per-instance — the primary
31
+ // and each /ctm-dev instance (/tmp/walle-dev-<port>) have distinct CTM_DATA_DIRs —
32
+ // so primary and dev never share a scrollback dir and the orphan sweep can't reach
33
+ // across instances. (On the primary CTM_DATA_DIR lives in Dropbox, so these files
34
+ // sync with the rest of the CTM data; that's intentional per the chosen layout.)
35
+
36
+ function _defaultDir() {
37
+ // Explicit override wins (used by tests and any deliberate placement).
38
+ const fromEnv = process.env.CTM_SCROLLBACK_DIR;
39
+ if (fromEnv && String(fromEnv).trim()) return path.resolve(String(fromEnv).trim());
40
+ const home = os.homedir() || process.env.HOME || os.tmpdir();
41
+ const dataDir = process.env.CTM_DATA_DIR || path.join(home, '.walle', 'data');
42
+ return path.join(dataDir, 'scrollback');
43
+ }
44
+
45
+ function _safeName(sessionId) {
46
+ // Session ids are uuids/slugs, but guard against path traversal regardless.
47
+ return String(sessionId || '').replace(/[^A-Za-z0-9._-]/g, '_');
48
+ }
49
+
50
+ function createScrollbackSnapshotStore(options = {}) {
51
+ const dir = options.dir ? path.resolve(String(options.dir)) : _defaultDir();
52
+ let _ensured = false;
53
+ let _writeSeq = 0;
54
+
55
+ function _ensureDir() {
56
+ if (_ensured) return;
57
+ fs.mkdirSync(dir, { recursive: true });
58
+ _ensured = true;
59
+ }
60
+
61
+ async function _ensureDirAsync() {
62
+ if (_ensured) return;
63
+ await fsp.mkdir(dir, { recursive: true });
64
+ _ensured = true;
65
+ }
66
+
67
+ // Shared input validation + payload/path prep for write()/writeAsync(). Returns
68
+ // null for invalid input (so both writers reject identically), else the prepared
69
+ // JSON + unique temp path + final target. The `++_writeSeq` runs here so two
70
+ // concurrent writes in one process can't collide on the temp path.
71
+ function _prepareWrite(sessionId, record) {
72
+ if (!sessionId || !record || typeof record.data !== 'string') return null;
73
+ const payload = {
74
+ sessionId: String(sessionId),
75
+ agentSessionId: record.agentSessionId || '',
76
+ cols: Number(record.cols) || 0,
77
+ rows: Number(record.rows) || 0,
78
+ fp: record.fp || '',
79
+ ts: Number(record.ts) || Date.now(),
80
+ data: record.data,
81
+ };
82
+ const target = pathFor(sessionId);
83
+ const tmp = `${target}.${process.pid}.${++_writeSeq}.tmp`;
84
+ return { json: JSON.stringify(payload), tmp, target };
85
+ }
86
+
87
+ function snapshotDir() { return dir; }
88
+
89
+ function pathFor(sessionId) {
90
+ return path.join(dir, `${_safeName(sessionId)}.scrollback.json`);
91
+ }
92
+
93
+ // Atomic full-overwrite write (temp + rename). `record` carries the serialized
94
+ // blob and identity header. Returns true on success, false on any fs error
95
+ // (best-effort — a failed snapshot must never break shutdown or a tick).
96
+ function write(sessionId, record) {
97
+ const prep = _prepareWrite(sessionId, record);
98
+ if (!prep) return false;
99
+ try {
100
+ _ensureDir();
101
+ fs.writeFileSync(prep.tmp, prep.json);
102
+ fs.renameSync(prep.tmp, prep.target);
103
+ return true;
104
+ } catch {
105
+ return false;
106
+ }
107
+ }
108
+
109
+ // Async sibling of write() — same atomic temp+rename, but the file I/O runs off
110
+ // the main event loop. The periodic snapshot sweep (every active session, every
111
+ // 30s) is the hot caller; on the primary the snapshot dir lives under CTM_DATA_DIR
112
+ // (Dropbox), so sync writeFileSync/renameSync there stalled the loop on Dropbox
113
+ // I/O latency. JSON.stringify stays synchronous (bounded CPU); only the I/O moves.
114
+ async function writeAsync(sessionId, record) {
115
+ const prep = _prepareWrite(sessionId, record);
116
+ if (!prep) return false;
117
+ try {
118
+ await _ensureDirAsync();
119
+ await fsp.writeFile(prep.tmp, prep.json);
120
+ await fsp.rename(prep.tmp, prep.target);
121
+ return true;
122
+ } catch {
123
+ return false;
124
+ }
125
+ }
126
+
127
+ // Read a session's snapshot record, or null if absent/unreadable/corrupt.
128
+ function read(sessionId) {
129
+ if (!sessionId) return null;
130
+ try {
131
+ const raw = fs.readFileSync(pathFor(sessionId), 'utf8');
132
+ const rec = JSON.parse(raw);
133
+ if (!rec || typeof rec.data !== 'string') return null;
134
+ return rec;
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+
140
+ function remove(sessionId) {
141
+ if (!sessionId) return;
142
+ try { fs.unlinkSync(pathFor(sessionId)); } catch { /* already gone */ }
143
+ }
144
+
145
+ // Session ids that currently have a snapshot file on disk (for orphan cleanup).
146
+ function listSessionIds() {
147
+ try {
148
+ return fs.readdirSync(dir)
149
+ .filter((f) => f.endsWith('.scrollback.json'))
150
+ .map((f) => f.slice(0, -'.scrollback.json'.length));
151
+ } catch {
152
+ return [];
153
+ }
154
+ }
155
+
156
+ return { write, writeAsync, read, remove, listSessionIds, pathFor, snapshotDir };
157
+ }
158
+
159
+ module.exports = { createScrollbackSnapshotStore };