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,51 @@
1
+ 'use strict';
2
+
3
+ function stripAnsi(value) {
4
+ return String(value || '')
5
+ .replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '')
6
+ .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
7
+ .replace(/\x1b[()][A-Z0-9]/g, '')
8
+ .replace(/\x1b>[0-9]*[a-zA-Z]/g, '')
9
+ .replace(/\x1b<[a-zA-Z]/g, '')
10
+ .replace(/\x1b=[0-9]*/g, '')
11
+ .replace(/\x1b\s/g, '')
12
+ .replace(/\r/g, '')
13
+ .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '');
14
+ }
15
+
16
+ function normalizeText(value) {
17
+ return stripAnsi(value)
18
+ .replace(/\u00a0/g, ' ')
19
+ .split('\n')
20
+ .map((line) => line.replace(/[ \t]+/g, ' ').replace(/\s+$/g, ''))
21
+ .join('\n')
22
+ .trim();
23
+ }
24
+
25
+ function normalizeAgentType(value) {
26
+ const text = String(value || '').trim().toLowerCase();
27
+ if (!text) return '';
28
+ if (text === 'codex' || text === 'codex-cli' || text === 'codex_cli' || text === 'codex_cli_rs') return 'codex';
29
+ return text;
30
+ }
31
+
32
+ function looksLikeCodexTranscriptPagerSnapshot(agentType, value) {
33
+ if (normalizeAgentType(agentType) !== 'codex') return false;
34
+ const text = normalizeText(value);
35
+ if (!text) return false;
36
+
37
+ const compact = text.replace(/[ \t]+/g, ' ');
38
+ const hasTranscriptHeading = /(?:^|\n)\s*(?:[\/\\]\s*){1,}\s*T\s*R\s*A\s*N\s*S\s*C\s*R\s*I\s*P\s*T\b/i.test(compact)
39
+ || /(?:^|\n)\s*T\s*R\s*A\s*N\s*S\s*C\s*R\s*I\s*P\s*T\s*(?:[\/\\]\s*){1,}/i.test(compact);
40
+ const hasQuitInstruction = /\bq\s+to\s+quit\b/i.test(compact);
41
+ const hasScrollInstruction = /\bj\/k\s+to\s+scroll\b/i.test(compact) || /\bpgup\/pgdn\s+to\s+page\b/i.test(compact);
42
+ const hasEditInstruction = /\benter\s+to\s+edit\s+message\b/i.test(compact)
43
+ || /\besc\/.{0,24}\bto\s+edit\s+(?:prev|previous|next)\b/i.test(compact);
44
+
45
+ return hasTranscriptHeading && hasQuitInstruction && hasScrollInstruction && hasEditInstruction;
46
+ }
47
+
48
+ module.exports = {
49
+ looksLikeCodexTranscriptPagerSnapshot,
50
+ normalizeText,
51
+ };
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ // Newer Codex CLI versions background-compress cold rollouts to
4
+ // `rollout-*.jsonl.zst` and re-materialize the live `.jsonl` on resume.
5
+ // CTM reads rollouts directly, so a compressed archive must be lazily
6
+ // materialized to a disk cache once and then served like any other rollout.
7
+ // Compressed rollouts are cold + immutable by definition — no tail/HWM
8
+ // semantics needed; the cache key embeds mtime+size so a replaced archive
9
+ // re-materializes.
10
+
11
+ const fs = require('fs');
12
+ const os = require('os');
13
+ const path = require('path');
14
+ const zlib = require('zlib');
15
+ const { spawnSync } = require('child_process');
16
+
17
+ const MAX_COMPRESSED_BYTES = 64 * 1024 * 1024; // skip absurd archives
18
+ const MAX_OUTPUT_BYTES = 512 * 1024 * 1024; // decompression bomb guard
19
+ const CACHE_MAX_FILES = 16;
20
+ const CACHE_MAX_BYTES = 1024 * 1024 * 1024;
21
+
22
+ function isZstRolloutFileName(name) {
23
+ return /\.jsonl\.zst$/i.test(String(name || ''));
24
+ }
25
+
26
+ function cacheDir() {
27
+ if (process.env.CTM_CODEX_ZST_CACHE_DIR) return process.env.CTM_CODEX_ZST_CACHE_DIR;
28
+ return path.join(os.homedir(), '.walle', 'cache', 'codex-zst');
29
+ }
30
+
31
+ let _capability = null;
32
+ function zstdCapability() {
33
+ if (_capability) return _capability;
34
+ if (typeof zlib.zstdDecompressSync === 'function') { _capability = 'zlib'; return _capability; }
35
+ try {
36
+ const probe = spawnSync('zstd', ['--version'], { stdio: 'ignore', timeout: 5000 });
37
+ if (probe.status === 0) { _capability = 'binary'; return _capability; }
38
+ } catch { /* not on PATH */ }
39
+ _capability = 'none';
40
+ return _capability;
41
+ }
42
+
43
+ function _decompress(buf) {
44
+ const cap = zstdCapability();
45
+ if (cap === 'zlib') return zlib.zstdDecompressSync(buf, { maxOutputLength: MAX_OUTPUT_BYTES });
46
+ if (cap === 'binary') {
47
+ const run = spawnSync('zstd', ['-dc'], { input: buf, maxBuffer: MAX_OUTPUT_BYTES, timeout: 120000 });
48
+ if (run.status === 0 && run.stdout) return run.stdout;
49
+ throw new Error(`zstd -dc failed: ${String(run.stderr || run.error || 'unknown').slice(0, 200)}`);
50
+ }
51
+ const err = new Error('no zstd decompressor (Node >= 22.15 zlib.zstdDecompressSync or the zstd binary required)');
52
+ err.code = 'NO_ZSTD';
53
+ throw err;
54
+ }
55
+
56
+ function _pruneCache(dir) {
57
+ let entries = [];
58
+ try {
59
+ entries = fs.readdirSync(dir)
60
+ .filter(name => name.endsWith('.jsonl'))
61
+ .map(name => {
62
+ const fullPath = path.join(dir, name);
63
+ try { const st = fs.statSync(fullPath); return { fullPath, size: st.size, mtimeMs: st.mtimeMs }; }
64
+ catch { return null; }
65
+ })
66
+ .filter(Boolean)
67
+ .sort((a, b) => b.mtimeMs - a.mtimeMs);
68
+ } catch { return; }
69
+ let totalBytes = 0;
70
+ entries.forEach((entry, idx) => {
71
+ totalBytes += entry.size;
72
+ if (idx >= CACHE_MAX_FILES || totalBytes > CACHE_MAX_BYTES) {
73
+ try { fs.unlinkSync(entry.fullPath); } catch { /* concurrent prune */ }
74
+ }
75
+ });
76
+ }
77
+
78
+ // `.zst` → cached decompressed `.jsonl` path; anything else passes through
79
+ // unchanged so callers can call this unconditionally.
80
+ function materializeCodexRollout(filePath) {
81
+ const p = String(filePath || '');
82
+ if (!isZstRolloutFileName(p)) return { path: p, ok: true, reason: '' };
83
+ let st;
84
+ try { st = fs.statSync(p); } catch { return { path: '', ok: false, reason: 'stat_failed' }; }
85
+ if (st.size > MAX_COMPRESSED_BYTES) return { path: '', ok: false, reason: 'too_large' };
86
+ if (zstdCapability() === 'none') return { path: '', ok: false, reason: 'no_zstd' };
87
+ const dir = cacheDir();
88
+ const key = `${path.basename(p).replace(/\.zst$/i, '').replace(/\.jsonl$/i, '')}-${Math.round(st.mtimeMs)}-${st.size}.jsonl`;
89
+ const out = path.join(dir, key);
90
+ try { if (fs.existsSync(out)) return { path: out, ok: true, reason: 'cache' }; } catch { /* fall through */ }
91
+ try {
92
+ fs.mkdirSync(dir, { recursive: true });
93
+ const decompressed = _decompress(fs.readFileSync(p));
94
+ const tmp = `${out}.tmp-${process.pid}`;
95
+ fs.writeFileSync(tmp, decompressed);
96
+ fs.renameSync(tmp, out);
97
+ _pruneCache(dir);
98
+ return { path: out, ok: true, reason: 'decompressed' };
99
+ } catch (e) {
100
+ return { path: '', ok: false, reason: `decompress_failed:${String(e.code || e.message || '').slice(0, 80)}` };
101
+ }
102
+ }
103
+
104
+ // Drop a `.zst` archive when its live `.jsonl` sibling exists (the
105
+ // resume-rematerialization case — the live file is authoritative).
106
+ function dropZstWithLiveSibling(filePaths) {
107
+ const list = Array.isArray(filePaths) ? filePaths : [];
108
+ const inSet = new Set(list);
109
+ return list.filter(p => {
110
+ if (!isZstRolloutFileName(p)) return true;
111
+ const live = p.replace(/\.zst$/i, '');
112
+ if (inSet.has(live)) return false;
113
+ try { return !fs.existsSync(live); } catch { return true; }
114
+ });
115
+ }
116
+
117
+ module.exports = {
118
+ isZstRolloutFileName,
119
+ zstdCapability,
120
+ materializeCodexRollout,
121
+ dropZstWithLiveSibling,
122
+ MAX_COMPRESSED_BYTES,
123
+ _pruneCache,
124
+ };
@@ -22,43 +22,130 @@ const CC_ALIAS_MAP = {
22
22
 
23
23
  const AGENT_MODEL_CACHE_TTL = 5000;
24
24
 
25
+ function _normalizeClaudeModelName(value) {
26
+ if (typeof value !== 'string') return null;
27
+ const alias = value.replace(/\[.*$/, '').trim().toLowerCase();
28
+ return CC_ALIAS_MAP[alias] || (alias.startsWith('claude-') ? alias : null);
29
+ }
30
+
25
31
  // Read the actual model used in the most recent assistant turn of a JSONL
26
32
  // transcript. This is the source of truth: it reflects what Claude Code
27
33
  // actually invoked, immune to alias drift.
28
- function getClaudeCodeModelFromJsonl(jsonlPath) {
34
+ // Walk a JSONL chunk backwards for the last assistant turn carrying a claude- model id.
35
+ function _scanJsonlForClaudeModelInfo(raw) {
36
+ const lines = raw.split('\n');
37
+ for (let i = lines.length - 1; i >= 0; i--) {
38
+ const line = lines[i];
39
+ if (!line || line[0] !== '{') continue;
40
+ // Cheap pre-filter so we don't JSON.parse every user/system line.
41
+ if (line.indexOf('"model"') === -1) continue;
42
+ try {
43
+ const rec = JSON.parse(line);
44
+ if (!rec || rec.type !== 'assistant') continue;
45
+ const m = rec && rec.message && rec.message.model;
46
+ const model = _normalizeClaudeModelName(m);
47
+ if (model) {
48
+ const timestampMs = typeof rec.timestamp === 'string' ? Date.parse(rec.timestamp) : NaN;
49
+ return {
50
+ model,
51
+ timestampMs: Number.isFinite(timestampMs) ? timestampMs : 0,
52
+ };
53
+ }
54
+ } catch { /* skip malformed line */ }
55
+ }
56
+ return null;
57
+ }
58
+
59
+ function _scanJsonlForClaudeModel(raw) {
60
+ const info = _scanJsonlForClaudeModelInfo(raw);
61
+ return info ? info.model : null;
62
+ }
63
+
64
+ // Read just the TAIL of the transcript by default: the model lives in the most
65
+ // recent assistant turn, so reading the whole file (an active session's JSONL is
66
+ // 50–100MB) on the main loop just to find the last line was a model-sync boot
67
+ // freeze — getSessionAgentModel runs this for every session on a cold cache.
68
+ const _CLAUDE_JSONL_MODEL_TAIL_BYTES = Math.max(
69
+ 64 * 1024,
70
+ Number(process.env.CTM_CLAUDE_MODEL_JSONL_TAIL_BYTES) || 512 * 1024
71
+ );
72
+
73
+ function getClaudeCodeModelInfoFromJsonl(jsonlPath) {
29
74
  if (!jsonlPath) return null;
30
75
  try {
31
- if (!fs.existsSync(jsonlPath)) return null;
32
- const raw = fs.readFileSync(jsonlPath, 'utf8');
33
- // Walk backwards through lines to find the last assistant turn with a model.
34
- const lines = raw.split('\n');
35
- for (let i = lines.length - 1; i >= 0; i--) {
36
- const line = lines[i];
37
- if (!line || line[0] !== '{') continue;
38
- // Cheap pre-filter so we don't JSON.parse every user/system line.
39
- if (line.indexOf('"model"') === -1) continue;
40
- try {
41
- const rec = JSON.parse(line);
42
- if (!rec || rec.type !== 'assistant') continue;
43
- const m = rec && rec.message && rec.message.model;
44
- if (typeof m === 'string' && m.startsWith('claude-')) return m;
45
- } catch { /* skip malformed line */ }
76
+ let size;
77
+ try { size = fs.statSync(jsonlPath).size; } catch { return null; } // missing/unreadable
78
+ if (size <= _CLAUDE_JSONL_MODEL_TAIL_BYTES) {
79
+ return _scanJsonlForClaudeModelInfo(fs.readFileSync(jsonlPath, 'utf8'));
46
80
  }
81
+ // Large file: read only the trailing window, dropping the leading partial line
82
+ // (the tail boundary almost certainly splits one).
83
+ const fd = fs.openSync(jsonlPath, 'r');
84
+ let tail;
85
+ try {
86
+ const buf = Buffer.allocUnsafe(_CLAUDE_JSONL_MODEL_TAIL_BYTES);
87
+ const read = fs.readSync(fd, buf, 0, _CLAUDE_JSONL_MODEL_TAIL_BYTES, size - _CLAUDE_JSONL_MODEL_TAIL_BYTES);
88
+ tail = buf.toString('utf8', 0, read);
89
+ } finally { fs.closeSync(fd); }
90
+ const nl = tail.indexOf('\n');
91
+ const found = _scanJsonlForClaudeModelInfo(nl >= 0 ? tail.slice(nl + 1) : tail);
92
+ if (found) return found;
93
+ // Rare: the last assistant-with-model turn is older than the tail window.
94
+ // Fall back to a full read so we are never LESS correct than before — only the
95
+ // (uncommon) miss pays the full cost the old code always paid.
96
+ return _scanJsonlForClaudeModelInfo(fs.readFileSync(jsonlPath, 'utf8'));
47
97
  } catch { /* file unreadable */ }
48
98
  return null;
49
99
  }
50
100
 
51
- function getClaudeCodeModel(homeDir = process.env.HOME) {
101
+ function getClaudeCodeModelFromJsonl(jsonlPath) {
102
+ const info = getClaudeCodeModelInfoFromJsonl(jsonlPath);
103
+ return info ? info.model : null;
104
+ }
105
+
106
+ function getClaudeCodeModelSelection(homeDir = process.env.HOME) {
107
+ const settingsPath = path.join(homeDir, '.claude', 'settings.json');
52
108
  try {
53
- const raw = JSON.parse(fs.readFileSync(path.join(homeDir, '.claude', 'settings.json'), 'utf8'));
54
- if (!raw.model) return null;
55
- const alias = raw.model.replace(/\[.*$/, '').trim().toLowerCase();
56
- return CC_ALIAS_MAP[alias] || (alias.startsWith('claude-') ? alias : null);
109
+ const raw = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
110
+ const model = _normalizeClaudeModelName(raw.model);
111
+ if (!model) return null;
112
+ let mtimeMs = 0;
113
+ try { mtimeMs = fs.statSync(settingsPath).mtimeMs || 0; } catch {}
114
+ return { model, mtimeMs, rawModel: raw.model, settingsPath };
57
115
  } catch {
58
116
  return null;
59
117
  }
60
118
  }
61
119
 
120
+ function getClaudeCodeModel(homeDir = process.env.HOME) {
121
+ const selection = getClaudeCodeModelSelection(homeDir);
122
+ return selection ? selection.model : null;
123
+ }
124
+
125
+ function shouldPreferClaudeSelectedModel(selection, jsonlInfo) {
126
+ if (!selection || !selection.model) return false;
127
+ if (!jsonlInfo || !jsonlInfo.model) return true;
128
+ if (!selection.mtimeMs || !jsonlInfo.timestampMs) return false;
129
+ return selection.mtimeMs > (jsonlInfo.timestampMs + 1000);
130
+ }
131
+
132
+ function _coerceTimeMs(value) {
133
+ if (!value) return 0;
134
+ if (typeof value === 'number' && Number.isFinite(value)) return value > 1e12 ? value : value * 1000;
135
+ const parsed = Date.parse(String(value));
136
+ return Number.isFinite(parsed) ? parsed : 0;
137
+ }
138
+
139
+ function getClaudeSessionModelName({ jsonlPath, homeDir = process.env.HOME, sessionActivityMs = 0 } = {}) {
140
+ const jsonlInfo = getClaudeCodeModelInfoFromJsonl(jsonlPath);
141
+ const selection = getClaudeCodeModelSelection(homeDir);
142
+ if (shouldPreferClaudeSelectedModel(selection, jsonlInfo)) {
143
+ const activityMs = _coerceTimeMs(sessionActivityMs);
144
+ if (!activityMs || activityMs >= (selection.mtimeMs - 10 * 60 * 1000)) return selection.model;
145
+ }
146
+ return (jsonlInfo && jsonlInfo.model) || (selection && selection.model) || null;
147
+ }
148
+
62
149
  function openCodexStateDb(homeDir = process.env.HOME) {
63
150
  const Database = require('better-sqlite3');
64
151
  const dbPath = path.join(homeDir, '.codex', 'state_5.sqlite');
@@ -147,7 +234,7 @@ function resolveModelToRegistry(modelName) {
147
234
  return { model_id: modelName, model_provider: provider };
148
235
  }
149
236
 
150
- function createCodingAgentModelSync({ sessions, dbModule, broadcastSessionList }) {
237
+ function createCodingAgentModelSync({ sessions, dbModule, broadcastSessionList, resolveClaudeJsonlPath, resolveAgentModelsOffThread }) {
151
238
  const agentModelCache = {};
152
239
  let agentModelCacheTime = 0;
153
240
 
@@ -156,11 +243,23 @@ function createCodingAgentModelSync({ sessions, dbModule, broadcastSessionList }
156
243
  if (!agentType) return null;
157
244
 
158
245
  const now = Date.now();
246
+
247
+ // For claude, resolve the transcript path up front. session.jsonlPath is
248
+ // only populated by the JSONL file-watcher on a write event, so idle and
249
+ // freshly-restored sessions lack it even though the transcript on disk
250
+ // already records the model — leaving the picker stuck on "(detecting…)".
251
+ // resolveClaudeJsonlPath derives the deterministic path
252
+ // (~/.claude/projects/<encoded-cwd>/<agentSessionId>.jsonl) so detection
253
+ // works without waiting for the next write.
254
+ const claudeJsonlPath = agentType === 'claude'
255
+ ? (session.jsonlPath || (resolveClaudeJsonlPath ? resolveClaudeJsonlPath(session) : '') || '')
256
+ : '';
257
+
159
258
  // Per-session cache key for claude too because JSONL fallback is
160
259
  // per-session. Codex also includes the matched thread id because it can
161
260
  // be discovered after spawn.
162
261
  const cacheKey = agentType === 'claude'
163
- ? `claude:${session.id}:${session.jsonlPath || ''}`
262
+ ? `claude:${session.id}:${claudeJsonlPath}`
164
263
  : agentType === 'codex'
165
264
  ? `codex:${session.id}:${session._claudeSessionId || ''}`
166
265
  : agentType;
@@ -172,13 +271,21 @@ function createCodingAgentModelSync({ sessions, dbModule, broadcastSessionList }
172
271
  let modelName = null;
173
272
  switch (agentType) {
174
273
  case 'claude':
175
- // Prefer settings.json (alias to CC_ALIAS_MAP) so a `/model` switch in
176
- // Claude Code reflects immediately, even before the next assistant
177
- // turn writes to JSONL. Fall back to the JSONL transcript when
178
- // settings.json is unreadable; that path is also our safety net if
179
- // CC_ALIAS_MAP goes stale on a future alias rotation, since JSONL
180
- // records the actual model the API resolved.
181
- modelName = getClaudeCodeModel() || getClaudeCodeModelFromJsonl(session.jsonlPath);
274
+ // The JSONL transcript is the historical source of truth because it
275
+ // records what the API actually resolved on the latest assistant turn.
276
+ // Claude Code also rewrites settings.json immediately when the user
277
+ // changes the selected model, before the next assistant turn exists.
278
+ // Prefer that newer live selection only when its mtime is newer than
279
+ // the latest assistant turn timestamp; otherwise keep JSONL precedence.
280
+ modelName = getClaudeSessionModelName({
281
+ jsonlPath: claudeJsonlPath,
282
+ sessionActivityMs: Math.max(
283
+ _coerceTimeMs(session.lastActivity),
284
+ _coerceTimeMs(session.lastPtyActivity),
285
+ _coerceTimeMs(session.updatedAt),
286
+ _coerceTimeMs(session.createdAt)
287
+ ),
288
+ });
182
289
  break;
183
290
  case 'codex':
184
291
  modelName = session._claudeSessionId
@@ -248,10 +355,103 @@ function createCodingAgentModelSync({ sessions, dbModule, broadcastSessionList }
248
355
  if (changed && typeof broadcastSessionList === 'function') broadcastSessionList();
249
356
  }
250
357
 
358
+ // Off-main-loop variant of syncCodingSessionModels. The fs/DB-heavy resolution (claude JSONL
359
+ // scan + Codex state-DB reads + getCodexThreadTitleById) runs on the read-pool worker via
360
+ // resolveAgentModelsOffThread; only the small DECIDE/APPLY work (model→registry mapping, the
361
+ // CTM-DB title decision, mutating the in-memory session) stays on main. On pool
362
+ // unavailable/timeout (returns null) we SKIP the tick rather than dragging the work back onto
363
+ // the loop — model/title detection is best-effort UI sugar and converges next tick.
364
+ async function syncCodingSessionModelsOffThread() {
365
+ if (typeof resolveAgentModelsOffThread !== 'function') return syncCodingSessionModels();
366
+ let changed = false;
367
+ const items = [];
368
+ const ctx = []; // parallel to items: { session, agentType, wantCodexTitle }
369
+ for (const [, session] of sessions) {
370
+ const agentType = detectAgentType(session.cmd);
371
+ if (!agentType) continue;
372
+ if (agentType === 'gemini') {
373
+ // Pure arg-parse + registry map — no fs/DB; resolve inline on main.
374
+ const model = getSessionAgentModel(session);
375
+ if (model && session.model_id !== model.model_id) {
376
+ session.model_id = model.model_id;
377
+ session.model_provider = model.model_provider;
378
+ changed = true;
379
+ }
380
+ continue;
381
+ }
382
+ const claudeJsonlPath = agentType === 'claude'
383
+ ? (session.jsonlPath || (resolveClaudeJsonlPath ? resolveClaudeJsonlPath(session) : '') || '')
384
+ : '';
385
+ // Codex title DECISION runs on main (cheap CTM-DB read). Only the actual Codex-DB title
386
+ // FETCH is offloaded (wantCodexTitle). Mirrors the sync version's branches exactly.
387
+ let wantCodexTitle = false;
388
+ if (agentType === 'codex' && !session._codexTitleSynced) {
389
+ const existingTitle = dbModule.getSessionTitleNew(session.id);
390
+ if (existingTitle && existingTitle.userRenamed) {
391
+ session._codexTitleSynced = true;
392
+ } else if (!shouldApplyCodexAutoTitle({ session, existingTitle })) {
393
+ const liveLabel = normalizeTitleText(session.label);
394
+ if (liveLabel
395
+ && !isGenericCodingSessionLabel(liveLabel, 'codex')
396
+ && (!existingTitle || normalizeTitleText(existingTitle.title) !== liveLabel)) {
397
+ dbModule.setSessionTitleNew(session.id, liveLabel, false);
398
+ changed = true;
399
+ }
400
+ session._codexTitleSynced = true;
401
+ } else {
402
+ wantCodexTitle = !!session._claudeSessionId;
403
+ }
404
+ }
405
+ items.push({
406
+ id: session.id,
407
+ agentType,
408
+ claudeJsonlPath,
409
+ sessionActivityMs: Math.max(
410
+ _coerceTimeMs(session.lastActivity),
411
+ _coerceTimeMs(session.lastPtyActivity),
412
+ _coerceTimeMs(session.updatedAt),
413
+ _coerceTimeMs(session.createdAt)
414
+ ),
415
+ codexThreadId: session._claudeSessionId || null,
416
+ wantCodexTitle,
417
+ });
418
+ ctx.push({ session, agentType, wantCodexTitle });
419
+ }
420
+ if (items.length === 0) {
421
+ if (changed && typeof broadcastSessionList === 'function') broadcastSessionList();
422
+ return;
423
+ }
424
+ const results = await resolveAgentModelsOffThread(items);
425
+ if (!results) {
426
+ // Pool unavailable/timeout → skip this tick (do not run the heavy resolve on main).
427
+ if (changed && typeof broadcastSessionList === 'function') broadcastSessionList();
428
+ return;
429
+ }
430
+ const byId = new Map(results.map((r) => [r.id, r]));
431
+ for (const { session, agentType, wantCodexTitle } of ctx) {
432
+ const r = byId.get(session.id);
433
+ if (!r) continue;
434
+ const model = resolveModelToRegistry(r.modelName);
435
+ if (model && session.model_id !== model.model_id) {
436
+ session.model_id = model.model_id;
437
+ session.model_provider = model.model_provider;
438
+ changed = true;
439
+ }
440
+ if (agentType === 'codex' && wantCodexTitle && r.codexTitle) {
441
+ session.label = `Codex: ${r.codexTitle}`;
442
+ session._codexTitleSynced = true;
443
+ dbModule.setSessionTitleNew(session.id, session.label, false);
444
+ changed = true;
445
+ }
446
+ }
447
+ if (changed && typeof broadcastSessionList === 'function') broadcastSessionList();
448
+ }
449
+
251
450
  return {
252
451
  detectAgentType,
253
452
  getSessionAgentModel,
254
453
  syncCodingSessionModels,
454
+ syncCodingSessionModelsOffThread,
255
455
  };
256
456
  }
257
457
 
@@ -260,6 +460,9 @@ module.exports = {
260
460
  detectAgentType,
261
461
  getClaudeCodeModel,
262
462
  getClaudeCodeModelFromJsonl,
463
+ getClaudeCodeModelInfoFromJsonl,
464
+ getClaudeCodeModelSelection,
465
+ getClaudeSessionModelName,
263
466
  getCodexThreadModel,
264
467
  getCodexThreadTitleById,
265
468
  getCodexLatestModel,