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
@@ -14,18 +14,60 @@ const MODEL_TIER_ENV = {
14
14
  quality: 'WALLE_MODEL_QUALITY',
15
15
  };
16
16
 
17
- function defaultClientModule() {
18
- return require(path.resolve(__dirname, '..', '..', 'wall-e', 'llm', 'client'));
17
+ // The starred default provider/model live in Wall-E's brain (set from the AI
18
+ // Providers page) and are served by GET /api/wall-e/status. CTM's process env
19
+ // (WALLE_PROVIDER/WALLE_MODEL from the launchd plist) goes stale the moment the
20
+ // user re-stars a provider, so background calls must ask Wall-E first and only
21
+ // fall back to env when Wall-E is unreachable. Cached briefly to keep the
22
+ // per-call overhead at one HTTP roundtrip per TTL, with a shorter negative TTL
23
+ // so a Wall-E restart doesn't pin us to env for long.
24
+ const WALLE_DEFAULTS_TTL_MS = 30000;
25
+ const WALLE_DEFAULTS_NEGATIVE_TTL_MS = 5000;
26
+ let _walleDefaultsCache = null; // { at, value: {provider, model} | null }
27
+
28
+ function _resetWalleDefaultsCache() {
29
+ _walleDefaultsCache = null;
19
30
  }
20
31
 
21
- function defaultBrain() {
32
+ async function fetchWalleStatusDefaults(env) {
33
+ const port = Number(env?.WALLE_PORT) || 3457;
34
+ const response = await fetch(`http://127.0.0.1:${port}/api/wall-e/status`, {
35
+ signal: AbortSignal.timeout(1500),
36
+ });
37
+ if (!response.ok) throw new Error(`Wall-E status returned ${response.status}`);
38
+ const json = await response.json();
39
+ return json?.data || json || {};
40
+ }
41
+
42
+ async function getWalleDefaultSelection(options = {}) {
43
+ const env = options.env || process.env;
44
+ if (String(env.CTM_BACKGROUND_LLM_WALLE_DEFAULTS || '').trim() === '0') return null;
45
+ const now = Date.now();
46
+ if (_walleDefaultsCache) {
47
+ const ttl = _walleDefaultsCache.value ? WALLE_DEFAULTS_TTL_MS : WALLE_DEFAULTS_NEGATIVE_TTL_MS;
48
+ if (now - _walleDefaultsCache.at < ttl) return _walleDefaultsCache.value;
49
+ }
50
+ let value = null;
22
51
  try {
23
- const brain = require(path.resolve(__dirname, '..', '..', 'wall-e', 'brain'));
24
- try { brain.getDb(); } catch { brain.initDb(); }
25
- return brain;
52
+ const fetchStatus = options.fetchWalleStatus || fetchWalleStatusDefaults;
53
+ const status = await fetchStatus(env);
54
+ const provider = sanitizeSetupProviderType(status?.walle_provider || '');
55
+ if (provider) {
56
+ value = { provider, model: typeof status?.walle_model === 'string' ? status.walle_model : '' };
57
+ }
26
58
  } catch {
27
- return null;
59
+ value = null;
28
60
  }
61
+ _walleDefaultsCache = { at: now, value };
62
+ return value;
63
+ }
64
+
65
+ function defaultClientModule() {
66
+ return require(path.resolve(__dirname, '..', '..', 'wall-e', 'llm', 'client'));
67
+ }
68
+
69
+ function defaultBrain() {
70
+ return null;
29
71
  }
30
72
 
31
73
  function getBrainKv(brain, key) {
@@ -112,10 +154,14 @@ function resolveBackgroundLlmSelection(options = {}) {
112
154
  || 'anthropic'
113
155
  ) || 'anthropic';
114
156
  const runtime = providerRuntimeState({ brain, env, provider });
157
+ // options.defaultModel is the starred default model from Wall-E status — the
158
+ // live value of the brain's walle_model KV, so it slots in at that priority,
159
+ // ahead of the potentially stale env.WALLE_MODEL.
115
160
  const requestedModel = options.model
116
161
  || selectTierModel(options.modelTier, provider, { brain, env, clientModule })
117
162
  || getBrainKv(brain, 'walle_model_' + provider)
118
163
  || getBrainKv(brain, 'walle_model')
164
+ || options.defaultModel
119
165
  || env.WALLE_MODEL
120
166
  || '';
121
167
  const model = clientModule.resolveCompatibleModel
@@ -182,15 +228,50 @@ function extractBackgroundLlmText(response) {
182
228
  return '';
183
229
  }
184
230
 
231
+ // Failure classes (wall-e/llm/provider-error.js) that mean "this provider can't
232
+ // serve background work right now" — worth one hop to the local fallback. This
233
+ // deliberately includes quota_exceeded and provider_rejected (a depleted
234
+ // account, e.g. DeepSeek "402 Insufficient Balance", classifies as the former;
235
+ // other 4xx rejections as the latter) — background work is best-effort, so any
236
+ // provider-side refusal is better served locally than dropped.
237
+ const FALLBACK_ELIGIBLE_CLASSES = new Set([
238
+ 'quota_exceeded', 'auth_error', 'rate_limited', 'provider_unavailable',
239
+ 'network', 'timeout', 'provider_rejected',
240
+ ]);
241
+
242
+ function classifyBackgroundLlmError(err, options = {}) {
243
+ if (err?.reason === 'no_provider_credentials') return 'auth_error';
244
+ try {
245
+ const classify = options.classifyError
246
+ || require(path.resolve(__dirname, '..', '..', 'wall-e', 'llm', 'provider-error')).classifyProviderError;
247
+ return classify(err)?.type || 'unknown';
248
+ } catch {
249
+ return 'unknown';
250
+ }
251
+ }
252
+
253
+ function backgroundLlmFallbackProvider(env) {
254
+ const raw = String(env?.CTM_BACKGROUND_LLM_FALLBACK_PROVIDER ?? 'ollama').trim().toLowerCase();
255
+ if (!raw || raw === '0' || raw === 'none' || raw === 'off') return '';
256
+ return sanitizeSetupProviderType(raw);
257
+ }
258
+
259
+ let _lastFallbackLogAt = 0;
260
+
185
261
  async function callBackgroundLlm(prompt, options = {}) {
186
262
  const clientModule = options.clientModule || defaultClientModule();
187
- const selection = resolveBackgroundLlmSelection({ ...options, clientModule });
188
- const createClient = options.createClient || clientModule.createClient;
189
- const client = createClient(selection.clientType, selection.config);
190
- const timeoutMs = options.timeoutMs || 60000;
191
- const signal = options.signal || AbortSignal.timeout(timeoutMs);
263
+ const env = options.env || process.env;
192
264
 
193
- try {
265
+ // Respect the starred default from the AI Providers page (Wall-E brain) over
266
+ // the process env, which only reflects the launchd plist at boot.
267
+ let walleDefaults = null;
268
+ if (!options.provider) walleDefaults = await getWalleDefaultSelection(options);
269
+
270
+ const attempt = async (selection) => {
271
+ const createClient = options.createClient || clientModule.createClient;
272
+ const client = createClient(selection.clientType, selection.config);
273
+ const timeoutMs = options.timeoutMs || 60000;
274
+ const signal = options.signal || AbortSignal.timeout(timeoutMs);
194
275
  const response = await client.chat({
195
276
  model: selection.model || undefined,
196
277
  system: options.system,
@@ -209,17 +290,63 @@ async function callBackgroundLlm(prompt, options = {}) {
209
290
  requestedModel: selection.requestedModel,
210
291
  modelAdjusted: selection.modelAdjusted,
211
292
  };
293
+ };
294
+
295
+ const resolveOptions = {
296
+ ...options,
297
+ clientModule,
298
+ provider: options.provider || walleDefaults?.provider || undefined,
299
+ defaultModel: options.defaultModel
300
+ || (walleDefaults?.provider && !options.provider ? walleDefaults.model : ''),
301
+ };
302
+
303
+ let selection = null;
304
+ let primaryError = null;
305
+ try {
306
+ selection = resolveBackgroundLlmSelection(resolveOptions);
307
+ return await attempt(selection);
212
308
  } catch (err) {
213
- err.provider = err.provider || selection.provider;
214
- err.model = err.model || selection.model || selection.requestedModel || '';
215
- err.clientType = err.clientType || selection.clientType;
216
- throw err;
309
+ primaryError = err;
310
+ }
311
+
312
+ const failedProvider = selection?.provider || primaryError.provider || resolveOptions.provider || '';
313
+ primaryError.provider = primaryError.provider || failedProvider;
314
+ primaryError.model = primaryError.model || selection?.model || selection?.requestedModel || '';
315
+ primaryError.clientType = primaryError.clientType || selection?.clientType || '';
316
+
317
+ // One hop to the local fallback provider (free, no surprise spend) when the
318
+ // configured provider can't serve — never on caller-initiated aborts.
319
+ const fallbackProvider = backgroundLlmFallbackProvider(env);
320
+ const callerAborted = !!(options.signal && options.signal.aborted);
321
+ if (!fallbackProvider || fallbackProvider === failedProvider || callerAborted) throw primaryError;
322
+ const errorClass = classifyBackgroundLlmError(primaryError, options);
323
+ if (!FALLBACK_ELIGIBLE_CLASSES.has(errorClass)) throw primaryError;
324
+
325
+ try {
326
+ const fallbackSelection = resolveBackgroundLlmSelection({
327
+ ...resolveOptions,
328
+ provider: fallbackProvider,
329
+ model: undefined, // primary's model hints don't apply to the fallback
330
+ defaultModel: walleDefaults?.provider === fallbackProvider ? walleDefaults.model : '',
331
+ });
332
+ const result = await attempt(fallbackSelection);
333
+ const now = Date.now();
334
+ if (now - _lastFallbackLogAt > 60000) {
335
+ _lastFallbackLogAt = now;
336
+ console.warn(`[background-llm] ${failedProvider} failed (${errorClass}: ${String(primaryError.message || '').slice(0, 120)}); served by fallback provider ${fallbackProvider}`);
337
+ }
338
+ return { ...result, fallbackFrom: failedProvider, fallbackReason: errorClass };
339
+ } catch (fallbackErr) {
340
+ primaryError.fallbackError = String(fallbackErr?.message || fallbackErr || '');
341
+ throw primaryError;
217
342
  }
218
343
  }
219
344
 
220
345
  module.exports = {
221
346
  callBackgroundLlm,
222
347
  extractBackgroundLlmText,
348
+ getWalleDefaultSelection,
223
349
  providerRuntimeState,
224
350
  resolveBackgroundLlmSelection,
351
+ _resetWalleDefaultsCache,
225
352
  };
@@ -0,0 +1,212 @@
1
+ 'use strict';
2
+
3
+ // Branch + worktree inventory: classify every local branch and worktree against
4
+ // the set of refs that any CTM session ever touched (live sessions, startup_tasks,
5
+ // agent_sessions). A ref is "orphaned" when NO session — live or in the DB — ever
6
+ // referenced it. Cleanup safety is conservative: only merged orphans are auto-safe.
7
+ //
8
+ // `classifyInventory` is PURE (no git, no DB) so it is fully unit-testable; the
9
+ // caller supplies git facts (branches/worktrees/mergedSet) and the linked-ref set.
10
+ // `gatherGitFacts` is the thin async git layer.
11
+
12
+ function normalizeBranch(value) {
13
+ return String(value || '').trim().replace(/^refs\/heads\//, '');
14
+ }
15
+
16
+ function isMainBranch(ref) {
17
+ const b = normalizeBranch(ref);
18
+ return b === 'main' || b === 'master';
19
+ }
20
+
21
+ // CTM-internal refs that should never be swept by the bulk "Clean orphaned (safe)"
22
+ // action even when merged + session-less: auto-snapshot/sync branches CTM creates
23
+ // (`ctm/pre-sync/*`) and underscore-prefixed bookkeeping refs (`_refs/...`,
24
+ // `_remote_main`). They still classify as orphaned (visible) — just not auto-safe.
25
+ function isInternalRef(ref) {
26
+ const b = normalizeBranch(ref);
27
+ if (!b) return false;
28
+ return b.startsWith('_') || b.startsWith('ctm/pre-sync/');
29
+ }
30
+
31
+ // Normalize a path for set membership compares. Callers should pass realpath-
32
+ // resolved paths when possible; this only strips trailing separators.
33
+ function normalizePathKey(p) {
34
+ const s = String(p || '').trim();
35
+ if (!s) return '';
36
+ return s.replace(/[\\/]+$/, '');
37
+ }
38
+
39
+ function ageDaysFrom(iso, now) {
40
+ if (!iso) return null;
41
+ const t = new Date(iso).getTime();
42
+ if (!Number.isFinite(t)) return null;
43
+ return Math.max(0, Math.floor((now - t) / 86400000));
44
+ }
45
+
46
+ /**
47
+ * @param {object} input
48
+ * @param {Array<{ref,head,committerDate}>} input.branches all local branches
49
+ * @param {Array<object>} input.worktrees rich worktrees (live)
50
+ * @param {Set<string>} input.mergedSet branch names merged into main
51
+ * @param {object} input.linked { branches:[{ref,source,sessionId,label,status}],
52
+ * worktreePaths:[{path,source,sessionId,label,status}] }
53
+ * @param {Set<string>} input.liveBranches branch names used by live sessions now
54
+ * @param {Set<string>} input.liveWorktreePaths worktree paths used by live sessions now
55
+ * @param {number} input.now ms epoch (caller-supplied; deterministic)
56
+ */
57
+ function classifyInventory(input = {}) {
58
+ const {
59
+ branches = [],
60
+ worktrees = [],
61
+ mergedSet = new Set(),
62
+ linked = {},
63
+ liveBranches = new Set(),
64
+ liveWorktreePaths = new Set(),
65
+ now = 0,
66
+ } = input;
67
+
68
+ // Index the linked-ref set.
69
+ const linkedBranch = new Map(); // ref -> {source, sessionId, label, status}
70
+ for (const b of (linked.branches || [])) {
71
+ const ref = normalizeBranch(b.ref);
72
+ if (ref && !linkedBranch.has(ref)) linkedBranch.set(ref, b);
73
+ }
74
+ const linkedWtByPath = new Map();
75
+ for (const w of (linked.worktreePaths || [])) {
76
+ const key = normalizePathKey(w.path);
77
+ if (key && !linkedWtByPath.has(key)) linkedWtByPath.set(key, w);
78
+ }
79
+ const liveBranchSet = new Set([...liveBranches].map(normalizeBranch).filter(Boolean));
80
+ const liveWtSet = new Set([...liveWorktreePaths].map(normalizePathKey).filter(Boolean));
81
+
82
+ // Live worktree truth: branch -> worktree.
83
+ const wtByBranch = new Map();
84
+ for (const wt of worktrees) {
85
+ const ref = normalizeBranch(wt && wt.branch);
86
+ if (ref) wtByBranch.set(ref, wt);
87
+ }
88
+
89
+ function linkInfoForBranch(ref, wtPathKey) {
90
+ const hasLive = liveBranchSet.has(ref) || (!!wtPathKey && liveWtSet.has(wtPathKey));
91
+ const rec = linkedBranch.get(ref)
92
+ || (wtPathKey ? linkedWtByPath.get(wtPathKey) : null)
93
+ || null;
94
+ const isLinked = hasLive || !!rec;
95
+ let source = '';
96
+ if (hasLive) source = 'live_session';
97
+ else if (rec) source = rec.source || 'session';
98
+ return {
99
+ linked: isLinked,
100
+ hasLiveSession: hasLive,
101
+ linkedSource: source,
102
+ sessionId: rec ? (rec.sessionId || '') : '',
103
+ sessionLabel: rec ? (rec.label || '') : '',
104
+ };
105
+ }
106
+
107
+ // ---- Classify worktrees (decorate, don't replace existing rich fields) ----
108
+ const classifiedWorktrees = worktrees.map(wt => {
109
+ const ref = normalizeBranch(wt && wt.branch);
110
+ const wtPathKey = normalizePathKey(wt && wt.path);
111
+ const info = linkInfoForBranch(ref, wtPathKey);
112
+ const isMain = !!(wt && (wt.isMain === true || isMainBranch(ref)));
113
+ const merged = ref ? mergedSet.has(ref) : false;
114
+ const isGhost = !!(wt && wt.isGhost);
115
+ const isOrphan = !isMain && !info.linked && !isGhost;
116
+ const dirty = Number((wt && wt.dirtyFiles) || 0);
117
+ const internal = isInternalRef(ref);
118
+ return {
119
+ ...wt,
120
+ orphan: isOrphan,
121
+ orphanMerged: merged,
122
+ internal,
123
+ linked: info.linked,
124
+ hasLiveSession: info.hasLiveSession,
125
+ linkedSource: info.linkedSource,
126
+ orphanSessionId: info.sessionId,
127
+ orphanSessionLabel: info.sessionLabel,
128
+ safeToClean: isOrphan && merged && dirty === 0 && !internal,
129
+ };
130
+ });
131
+
132
+ // ---- Classify branches (all local branches, incl. those with worktrees) ----
133
+ const classifiedBranches = branches.map(b => {
134
+ const ref = normalizeBranch(b.ref);
135
+ const wt = wtByBranch.get(ref) || null;
136
+ const wtPathKey = wt ? normalizePathKey(wt.path) : '';
137
+ const info = linkInfoForBranch(ref, wtPathKey);
138
+ const isMain = isMainBranch(ref);
139
+ const merged = mergedSet.has(ref);
140
+ const hasWorktree = !!wt;
141
+ const isOrphan = !isMain && !info.linked;
142
+ const internal = isInternalRef(ref);
143
+ return {
144
+ ref,
145
+ head: b.head || '',
146
+ ageDays: ageDaysFrom(b.committerDate, now),
147
+ committerDate: b.committerDate || '',
148
+ hasWorktree,
149
+ worktreePath: wt ? wt.path : '',
150
+ merged,
151
+ isMain,
152
+ internal,
153
+ linked: info.linked,
154
+ hasLiveSession: info.hasLiveSession,
155
+ linkedSource: info.linkedSource,
156
+ sessionId: info.sessionId,
157
+ sessionLabel: info.sessionLabel,
158
+ isOrphan,
159
+ // Bare-branch deletion is auto-safe only when merged + orphaned + no
160
+ // worktree, and not a CTM-internal snapshot/bookkeeping ref.
161
+ safeToClean: isOrphan && merged && !hasWorktree && !internal,
162
+ };
163
+ });
164
+
165
+ const orphanWorktreeRefs = new Set(
166
+ classifiedWorktrees.filter(w => w.orphan && w.branch).map(w => normalizeBranch(w.branch))
167
+ );
168
+ const counts = {
169
+ totalBranches: classifiedBranches.length,
170
+ bareBranches: classifiedBranches.filter(b => !b.hasWorktree && !b.isMain).length,
171
+ orphanBranches: classifiedBranches.filter(b => b.isOrphan).length,
172
+ safeBranches: classifiedBranches.filter(b => b.safeToClean).length,
173
+ orphanWorktrees: classifiedWorktrees.filter(w => w.orphan).length,
174
+ safeWorktrees: classifiedWorktrees.filter(w => w.safeToClean).length,
175
+ };
176
+ // orphanTotal counts distinct refs (a branch + its worktree are one thing).
177
+ counts.orphanTotal = counts.orphanWorktrees
178
+ + classifiedBranches.filter(b => b.isOrphan && !orphanWorktreeRefs.has(b.ref)).length;
179
+ counts.safeTotal = counts.safeBranches + counts.safeWorktrees;
180
+
181
+ return { branches: classifiedBranches, worktrees: classifiedWorktrees, counts };
182
+ }
183
+
184
+ /**
185
+ * Gather raw git facts needed for classification. `git` is git-utils' `git(cwd,args)`.
186
+ * Returns { branches:[{ref,head,committerDate}], mergedSet:Set }.
187
+ */
188
+ async function gatherGitFacts(cwd, git, mainBranch = 'main') {
189
+ const SEP = String.fromCharCode(31); // unit separator (iso8601 dates contain spaces)
190
+ const fmt = `%(refname:short)${SEP}%(objectname:short)${SEP}%(committerdate:iso8601)`;
191
+ const [refsOut, mergedOut] = await Promise.all([
192
+ git(cwd, ['for-each-ref', `--format=${fmt}`, 'refs/heads']).catch(() => ''),
193
+ git(cwd, ['branch', '--merged', mainBranch, '--format=%(refname:short)']).catch(() => ''),
194
+ ]);
195
+ const branches = String(refsOut || '').split('\n').filter(Boolean).map(line => {
196
+ const [ref, head, committerDate] = line.split(SEP);
197
+ return { ref: normalizeBranch(ref), head: head || '', committerDate: committerDate || '' };
198
+ });
199
+ const mergedSet = new Set(
200
+ String(mergedOut || '').split('\n').map(s => normalizeBranch(s)).filter(Boolean)
201
+ );
202
+ return { branches, mergedSet };
203
+ }
204
+
205
+ module.exports = {
206
+ classifyInventory,
207
+ gatherGitFacts,
208
+ normalizeBranch,
209
+ isMainBranch,
210
+ isInternalRef,
211
+ normalizePathKey,
212
+ };
@@ -245,10 +245,22 @@ function readCacheConversations(appDir) {
245
245
  try { stat = fs.statSync(filePath); } catch { continue; }
246
246
  if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_CACHE_FILE_SIZE) continue;
247
247
 
248
+ // Probe only the 4096-byte HEAD first: the Electron HTTP cache holds hundreds of files
249
+ // and almost none are chat conversations. Reading every file IN FULL just to test the
250
+ // head was ~15s of synchronous readFileSync at boot (named by the fs-census:
251
+ // readFileSync@claude-desktop-sessions.js — 408 full reads). Read the head via one
252
+ // bounded readSync, and only fall back to a full read when the marker actually matches.
248
253
  let buf;
249
- try { buf = fs.readFileSync(filePath); } catch { continue; }
250
- const head = buf.subarray(0, Math.min(buf.length, 4096)).toString('latin1');
251
- if (!/chat_conversations/i.test(head)) continue;
254
+ let fd = -1;
255
+ try {
256
+ fd = fs.openSync(filePath, 'r');
257
+ const headLen = Math.min(stat.size, 4096);
258
+ const headBuf = Buffer.allocUnsafe(headLen);
259
+ const n = fs.readSync(fd, headBuf, 0, headLen, 0); // only inspect the bytes actually read
260
+ if (!/chat_conversations/i.test(headBuf.subarray(0, n).toString('latin1'))) { fs.closeSync(fd); continue; }
261
+ buf = fs.readFileSync(filePath); // marker present → now read the whole file
262
+ } catch { if (fd >= 0) { try { fs.closeSync(fd); } catch {} } continue; }
263
+ try { fs.closeSync(fd); } catch {}
252
264
 
253
265
  const json = extractJsonFromCacheBuffer(buf);
254
266
  if (!json) continue;
@@ -0,0 +1,151 @@
1
+ 'use strict';
2
+ // Safe coalescing of obsolete synchronized TUI frames.
3
+ //
4
+ // A synchronized frame is the byte span from `\x1b[?2026h` (begin synchronized
5
+ // update) to the matching `\x1b[?2026l` (end synchronized update). Within a
6
+ // single output batch a TUI may emit many such frames.
7
+ //
8
+ // THE ONLY SAFE COALESCING RULE (provably correct, no information loss):
9
+ // A synchronized frame that BEGINS with a full-screen erase (`\x1b[2J`,
10
+ // optionally accompanied by `\x1b[3J` scrollback-erase and/or a cursor-home
11
+ // `\x1b[H` / `\x1b[1;1H`) WIPES the entire screen. Therefore every COMPLETE
12
+ // synchronized frame that PRECEDES the LAST such full-clear frame in the
13
+ // batch is fully superseded and can be dropped without information loss.
14
+ //
15
+ // Partial-update frames (no leading full clear) are NEVER dropped unless a
16
+ // LATER full-clear supersedes them. This preserves the picker-clear /
17
+ // styling-restore / prompt-repair behavior that "latest frame wins" would
18
+ // destroy. Frames AFTER the last full-clear are always preserved (they paint
19
+ // on top of the cleared screen).
20
+ //
21
+ // Parsing is index-scan based (no catastrophic-backtracking regex) so huge
22
+ // batches with hundreds of frames stay cheap.
23
+
24
+ const SYNC_ON = '\x1b[?2026h';
25
+ const SYNC_OFF = '\x1b[?2026l';
26
+
27
+ // Matches a leading full-screen erase region: any ordering/combination of
28
+ // cursor-home + `\x1b[2J` (+ optional `\x1b[3J`), with optional surrounding
29
+ // home moves. We require at least one `\x1b[2J`. Bounded, linear-time regex
30
+ // anchored at start of the post-SYNC_ON content.
31
+ const CURSOR_HOME_RE = /^(?:\x1b\[H|\x1b\[1;1H)/;
32
+ const ERASE_DISPLAY_2J_RE = /^\x1b\[2J/;
33
+ const ERASE_SCROLLBACK_3J_RE = /^\x1b\[3J/;
34
+
35
+ // Returns true if `content` (the bytes immediately following `\x1b[?2026h`)
36
+ // begins with a full-screen clear, tolerating leading/interleaved cursor-home
37
+ // and scrollback-erase sequences around the mandatory `\x1b[2J`.
38
+ function beginsWithFullScreenClear(content) {
39
+ let i = 0;
40
+ let saw2J = false;
41
+ // Consume any leading run of cursor-home / 2J / 3J sequences. We only treat
42
+ // the frame as a full clear if a `\x1b[2J` appears in that leading run.
43
+ // Allowing 3J/home before or after 2J covers `\x1b[2J\x1b[3J\x1b[H`,
44
+ // `\x1b[3J\x1b[2J`, `\x1b[H\x1b[2J`, etc.
45
+ for (;;) {
46
+ const rest = content.slice(i);
47
+ let m;
48
+ if ((m = ERASE_DISPLAY_2J_RE.exec(rest))) {
49
+ saw2J = true;
50
+ i += m[0].length;
51
+ continue;
52
+ }
53
+ if ((m = ERASE_SCROLLBACK_3J_RE.exec(rest))) {
54
+ i += m[0].length;
55
+ continue;
56
+ }
57
+ if ((m = CURSOR_HOME_RE.exec(rest))) {
58
+ i += m[0].length;
59
+ continue;
60
+ }
61
+ break;
62
+ }
63
+ return saw2J;
64
+ }
65
+
66
+ // Tokenize `batch` into ordered segments. Each segment is one of:
67
+ // { type: 'frame', text, fullClear } — a COMPLETE synchronized frame
68
+ // { type: 'raw', text } — inter-frame bytes, OR a trailing
69
+ // INCOMPLETE frame (SYNC_ON with no
70
+ // following SYNC_OFF)
71
+ function tokenize(batch) {
72
+ const segments = [];
73
+ let pos = 0;
74
+ const len = batch.length;
75
+ while (pos < len) {
76
+ const onIdx = batch.indexOf(SYNC_ON, pos);
77
+ if (onIdx === -1) {
78
+ // No more frames — remaining bytes are raw.
79
+ segments.push({ type: 'raw', text: batch.slice(pos) });
80
+ break;
81
+ }
82
+ // Raw bytes before this frame's SYNC_ON.
83
+ if (onIdx > pos) {
84
+ segments.push({ type: 'raw', text: batch.slice(pos, onIdx) });
85
+ }
86
+ const contentStart = onIdx + SYNC_ON.length;
87
+ const offIdx = batch.indexOf(SYNC_OFF, contentStart);
88
+ if (offIdx === -1) {
89
+ // Trailing INCOMPLETE frame — keep as raw, never drop.
90
+ segments.push({ type: 'raw', text: batch.slice(onIdx) });
91
+ break;
92
+ }
93
+ const frameEnd = offIdx + SYNC_OFF.length;
94
+ const content = batch.slice(contentStart, offIdx);
95
+ segments.push({
96
+ type: 'frame',
97
+ text: batch.slice(onIdx, frameEnd),
98
+ fullClear: beginsWithFullScreenClear(content),
99
+ });
100
+ pos = frameEnd;
101
+ }
102
+ return segments;
103
+ }
104
+
105
+ // Coalesce obsolete synchronized frames in `batch`.
106
+ // Returns a (possibly shorter) string. Never throws; returns the original
107
+ // batch unchanged when there is nothing safe to drop.
108
+ function coalesceSyncFrames(batch) {
109
+ const input = String(batch == null ? '' : batch);
110
+ if (!input) return input;
111
+ // Cheap bail-out: need at least two SYNC_ON markers AND a full clear marker
112
+ // for any coalescing to be possible.
113
+ if (input.indexOf('\x1b[2J') === -1) return input;
114
+
115
+ const segments = tokenize(input);
116
+
117
+ // Find index of the LAST complete frame that begins with a full-screen clear.
118
+ let lastFullClearIdx = -1;
119
+ for (let i = 0; i < segments.length; i++) {
120
+ const seg = segments[i];
121
+ if (seg.type === 'frame' && seg.fullClear) lastFullClearIdx = i;
122
+ }
123
+ if (lastFullClearIdx === -1) return input; // no full-clear frame → unchanged.
124
+
125
+ // Are there any COMPLETE frames before the full-clear frame to drop?
126
+ let droppable = 0;
127
+ for (let i = 0; i < lastFullClearIdx; i++) {
128
+ if (segments[i].type === 'frame') droppable++;
129
+ }
130
+ if (droppable === 0) return input; // nothing superseded → unchanged.
131
+
132
+ // Rebuild: drop COMPLETE frames before the last full-clear frame. Keep
133
+ // everything else (raw segments, the full-clear frame, and all segments
134
+ // after it) in place.
135
+ let out = '';
136
+ for (let i = 0; i < segments.length; i++) {
137
+ const seg = segments[i];
138
+ if (i < lastFullClearIdx && seg.type === 'frame') continue; // superseded
139
+ out += seg.text;
140
+ }
141
+ return out;
142
+ }
143
+
144
+ module.exports = {
145
+ coalesceSyncFrames,
146
+ // exported for unit testing of the internals
147
+ _tokenize: tokenize,
148
+ _beginsWithFullScreenClear: beginsWithFullScreenClear,
149
+ SYNC_ON,
150
+ SYNC_OFF,
151
+ };