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
@@ -105,7 +105,7 @@ function buildRawMessage(err) {
105
105
  }
106
106
 
107
107
  function classifyType(status, lower) {
108
- if (/insufficient[_ -]?quota|quota exceeded|quota_exceeded|resource[_ -]?exhausted|billing|credit|prepayment credits are depleted/i.test(lower)) return 'quota_exceeded';
108
+ if (/insufficient[_ -]?(quota|balance|funds)|quota exceeded|quota_exceeded|resource[_ -]?exhausted|billing|credit|prepayment credits are depleted/i.test(lower)) return 'quota_exceeded';
109
109
  if (status === 429 || /rate[_ -]?limit|too many requests|too_many_requests|rate_limit/i.test(lower)) return 'rate_limited';
110
110
  if (
111
111
  status === 401
@@ -186,6 +186,10 @@ function classifyProviderError(err, context = {}) {
186
186
  const copy = messageForType(type);
187
187
  const provider = context.provider || err?.provider || err?.providerType || null;
188
188
  const model = context.model || err?.model || null;
189
+ const providerId = context.providerId || err?.providerId || err?.provider_id || null;
190
+ const registryId = context.registryId || err?.registryId || err?.registry_id || null;
191
+ const routeLabel = context.routeLabel || err?.routeLabel || err?.route_label || null;
192
+ const connectionKind = context.connectionKind || err?.connectionKind || err?.connection_kind || null;
189
193
  const providerName = providerLabel(provider);
190
194
  const modelSuffix = model ? ` (${model})` : '';
191
195
  const retryAfter = extractRetryAfter(err);
@@ -200,6 +204,10 @@ function classifyProviderError(err, context = {}) {
200
204
  rawMessage,
201
205
  status,
202
206
  provider,
207
+ providerId,
208
+ registryId,
209
+ routeLabel,
210
+ connectionKind,
203
211
  model,
204
212
  retryAfter,
205
213
  actionLabel: 'Open Setup',
@@ -251,6 +259,9 @@ function recordProviderFailureAlert(providerError, brain) {
251
259
  if (!providerError) return { alerted: false };
252
260
  try {
253
261
  const planner = require('../skills/skill-planner');
262
+ const actionUrl = providerError.type === 'request_invariant'
263
+ ? null
264
+ : (providerError.actionUrl || '/setup.html');
254
265
  planner.addServiceAlert({
255
266
  service: 'ai_provider',
256
267
  type: `ai_provider:${providerError.provider || 'default'}:${providerError.type}`,
@@ -259,7 +270,7 @@ function recordProviderFailureAlert(providerError, brain) {
259
270
  provider: providerError.provider || null,
260
271
  model: providerError.model || null,
261
272
  message: `${providerError.title}: ${providerError.userMessage}`,
262
- action_url: providerError.actionUrl || '/setup.html',
273
+ action_url: actionUrl,
263
274
  provider_error: providerError,
264
275
  });
265
276
  return { alerted: true };
@@ -268,9 +279,26 @@ function recordProviderFailureAlert(providerError, brain) {
268
279
  }
269
280
  }
270
281
 
282
+ // One-line, secret-redacted human detail of a failed-provider error — for the
283
+ // routing-notice banner, so a degraded default surfaces WHY ("HTTP 404: anthropic
284
+ // error: model deepseek-v4-flash") instead of a bare "unreachable". Never throws.
285
+ function fallbackErrorDetail(err) {
286
+ if (!err) return '';
287
+ let raw = '';
288
+ try { raw = buildRawMessage(err); }
289
+ catch { try { raw = redactSecrets(String((err && err.message) || err || '')); } catch { raw = ''; } }
290
+ raw = String(raw || '').replace(/\s+/g, ' ').trim();
291
+ if (!raw) return '';
292
+ let status = 0;
293
+ try { status = extractStatus(err); } catch { status = 0; }
294
+ if (status && !new RegExp('\\b' + status + '\\b').test(raw)) raw = `HTTP ${status}: ${raw}`;
295
+ return raw.slice(0, 200);
296
+ }
297
+
271
298
  module.exports = {
272
299
  classifyProviderError,
273
300
  decorateProviderError,
301
+ fallbackErrorDetail,
274
302
  providerLabel,
275
303
  recordProviderFailureAlert,
276
304
  redactSecrets,
@@ -132,7 +132,11 @@ function modelMatchesProvider(model, providerId) {
132
132
  if (!model || !providerId) return true;
133
133
  const detected = detectProviderForModel(model);
134
134
  if (!detected) return true; // No claim — accept.
135
- return detected === providerId;
135
+ const provider = String(providerId || '').toLowerCase();
136
+ if (provider === 'openai-compatible' || provider.startsWith('openai-compatible-')) {
137
+ return detected === 'openai';
138
+ }
139
+ return detected === provider;
136
140
  }
137
141
 
138
142
  /** Test helper. */
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ const ANTHROPIC_SAMPLING_PARAMS = Object.freeze([
4
+ 'temperature',
5
+ 'top_p',
6
+ 'topP',
7
+ 'top_k',
8
+ 'topK',
9
+ ]);
10
+
11
+ function normalizeModelId(model) {
12
+ if (!model) return '';
13
+ let id = typeof model === 'string' ? model : String(model.id || model.model || model.name || '');
14
+ id = id.trim();
15
+ id = id.replace(/^@anthropic\//i, '');
16
+ id = id.replace(/^anthropic[.:/]/i, '');
17
+ id = id.replace(/@(default|[a-z0-9_.-]+)$/i, '');
18
+ return id.toLowerCase();
19
+ }
20
+
21
+ function anthropicModelRejectsSamplingParams(model) {
22
+ const id = normalizeModelId(model);
23
+ const match = id.match(/^claude-(?:opus|sonnet|haiku)-4-(\d+)(?:-|$)/);
24
+ return Boolean(match && Number(match[1]) >= 7);
25
+ }
26
+
27
+ function stripParams(request, keys, reason) {
28
+ const stripped = [];
29
+ for (const key of keys) {
30
+ if (Object.prototype.hasOwnProperty.call(request, key) && request[key] !== undefined) {
31
+ delete request[key];
32
+ stripped.push(key);
33
+ }
34
+ }
35
+ return stripped.length > 0 ? [{ reason, keys: stripped }] : [];
36
+ }
37
+
38
+ function stripAnthropicUnsupportedParams(request = {}, { model = request.model, force = false } = {}) {
39
+ const next = { ...request };
40
+ const shouldStrip = force || anthropicModelRejectsSamplingParams(model);
41
+ if (!shouldStrip) return { request: next, warnings: [] };
42
+ const warnings = stripParams(
43
+ next,
44
+ ANTHROPIC_SAMPLING_PARAMS,
45
+ `Removed Anthropic sampling parameter(s) unsupported by ${normalizeModelId(model) || 'selected model'}.`
46
+ );
47
+ return { request: next, warnings };
48
+ }
49
+
50
+ function isAnthropicSamplingParamError(error) {
51
+ const text = [
52
+ error?.message,
53
+ error?.responseBody,
54
+ error?.body,
55
+ JSON.stringify(error?.error || ''),
56
+ ].filter(Boolean).join('\n').toLowerCase();
57
+ return /(?:temperature|top[_ ]?p|top[_ ]?k).*(?:deprecated|not supported|unsupported|invalid)/i.test(text)
58
+ || /(?:deprecated|not supported|unsupported|invalid).*(?:temperature|top[_ ]?p|top[_ ]?k)/i.test(text);
59
+ }
60
+
61
+ module.exports = {
62
+ ANTHROPIC_SAMPLING_PARAMS,
63
+ anthropicModelRejectsSamplingParams,
64
+ isAnthropicSamplingParamError,
65
+ normalizeModelId,
66
+ stripAnthropicUnsupportedParams,
67
+ };
@@ -38,6 +38,32 @@ const KV_BACKFILL_DONE = 'backfill:done';
38
38
 
39
39
  const VALID_DOMAINS = ['work', 'personal', 'social', 'technical', 'admin'];
40
40
 
41
+ // ── Cheap "work remaining" signals ──────────────────────────────────────────
42
+ // This loop runs every 30s ON THE MAIN THREAD, and better-sqlite3 is synchronous,
43
+ // so any unbounded query here blocks Wall-E's entire event loop — HTTP included —
44
+ // for the full duration of the scan. On a multi-GB brain that means the daemon
45
+ // stops answering /api/* (e.g. the setup page's provider list) for tens of seconds
46
+ // at a time. So every per-tick "is there work left?" check MUST stay bounded — no
47
+ // full-table content scans.
48
+ //
49
+ // `length(content) > 10` forces reading each candidate row's (often overflow-paged)
50
+ // content, so counting embeddable memories is the one unavoidable full scan. It
51
+ // only moves as memories are ingested, so cache it briefly instead of paying it
52
+ // every tick.
53
+ let _embeddableCache = { value: null, at: 0 };
54
+ const EMBEDDABLE_TTL_MS = 10 * 60 * 1000;
55
+ function embeddableMemoryTotal() {
56
+ const now = Date.now();
57
+ if (_embeddableCache.value != null && now - _embeddableCache.at < EMBEDDABLE_TTL_MS) {
58
+ return _embeddableCache.value;
59
+ }
60
+ const c = brain.getDb().prepare(
61
+ "SELECT count(*) AS c FROM memories WHERE content IS NOT NULL AND length(content) > 10"
62
+ ).get().c;
63
+ _embeddableCache = { value: c, at: now };
64
+ return c;
65
+ }
66
+
41
67
  /**
42
68
  * Run one backfill iteration. Call periodically from the daemon.
43
69
  * Returns { classified, embedded, done } counts.
@@ -79,7 +105,10 @@ async function runOnce(opts = {}) {
79
105
  // Phase 2: Embed memories missing embeddings — run multiple batches per tick
80
106
  // Quick Ollama readiness check — after laptop sleep, Ollama needs a few seconds
81
107
  // to reload. Skip this tick rather than wasting it on failed/slow batches.
82
- if (embeddings && embeddings.isAvailable() && await _isEmbeddingProviderReady()) {
108
+ // Gate on the cheap remaining-count BEFORE the readiness probe: when embedding is
109
+ // already complete, embedBatch's anti-join would otherwise SCAN the whole memories
110
+ // table just to return 0 every tick. countUnembedded() is bounded (indexed counts).
111
+ if (embeddings && embeddings.isAvailable() && countUnembedded() > 0 && await _isEmbeddingProviderReady()) {
83
112
  const MAX_BATCHES_PER_TICK = 5; // Up to 5 x EMBED_BATCH per tick for faster backfill
84
113
  for (let b = 0; b < MAX_BATCHES_PER_TICK; b++) {
85
114
  try {
@@ -147,12 +176,19 @@ async function runOnce(opts = {}) {
147
176
  async function classifyBatch(opts = {}) {
148
177
  const db = brain.getDb();
149
178
 
150
- const cursor = brain.getKv(KV_CLASSIFY_CURSOR) || '0';
179
+ // Self-draining: select straight from the unclassified set (domain IS NULL) with a
180
+ // plain LIMIT — no `id > cursor` / `ORDER BY id ASC`. The old cursor+ORDER BY form
181
+ // made SQLite drive off idx_memories_domain and build a TEMP B-TREE over EVERY
182
+ // unclassified row (reading each one's content via a table seek) before applying
183
+ // LIMIT — so a single call read 80k+ rows' content and blocked the event loop for
184
+ // tens of seconds. classifyMemory always assigns a non-null domain, so classified
185
+ // rows leave the set and the next LIMIT picks up where this one left off. This is
186
+ // bounded to CLASSIFY_BATCH content reads per call and can't strand rows.
151
187
  const batch = db.prepare(`
152
188
  SELECT id, content, source, source_channel FROM memories
153
- WHERE domain IS NULL AND content IS NOT NULL AND length(content) > 10 AND id > ?
154
- ORDER BY id ASC LIMIT ?
155
- `).all(cursor, CLASSIFY_BATCH);
189
+ WHERE domain IS NULL AND content IS NOT NULL AND length(content) > 10
190
+ LIMIT ?
191
+ `).all(CLASSIFY_BATCH);
156
192
 
157
193
  if (batch.length === 0) return 0;
158
194
 
@@ -165,7 +201,6 @@ async function classifyBatch(opts = {}) {
165
201
  });
166
202
  classify();
167
203
 
168
- brain.setKv(KV_CLASSIFY_CURSOR, batch[batch.length - 1].id);
169
204
  return batch.length;
170
205
  }
171
206
 
@@ -358,24 +393,33 @@ async function indexBatch() {
358
393
  }
359
394
 
360
395
  const NOISE_BATCH = 500;
396
+ const KV_NOISE_DONE = 'backfill:noise:done';
361
397
 
362
398
  async function noiseStripBatch() {
399
+ // One-time legacy sweep: new content is stripped at ingest, so once we've swept the
400
+ // whole table there's nothing left to do — skip entirely instead of re-scanning.
401
+ if (brain.getKv(KV_NOISE_DONE) === '1') return 0;
402
+
363
403
  const db = brain.getDb();
364
404
  const cursor = brain.getKv(KV_NOISE_CURSOR) || '0';
365
- // Only process memories that have system-reminder tags in content (legacy noisy content)
366
- const batch = db.prepare(`
367
- SELECT id, content FROM memories
368
- WHERE content IS NOT NULL AND length(content) > 50
369
- AND content LIKE '%<system-reminder>%'
370
- AND id > ?
371
- ORDER BY id ASC LIMIT ?
372
- `).all(cursor, NOISE_BATCH);
373
- if (batch.length === 0) return 0;
405
+ // Scan a bounded id-window (LIMIT applies to SCANNED rows) and match the legacy
406
+ // <system-reminder> tag in JS. The old query put `content LIKE '%…%'` in SQL, so with
407
+ // only a handful of tagged rows it never filled the LIMIT and the cursor — advanced to
408
+ // the last MATCHED id barely moved, making every 30s tick re-scan the rest of the
409
+ // table reading content. Advancing past the last SCANNED row guarantees forward
410
+ // progress and lets the sweep terminate.
411
+ const batch = db.prepare(
412
+ 'SELECT id, content FROM memories WHERE id > ? ORDER BY id ASC LIMIT ?'
413
+ ).all(cursor, NOISE_BATCH);
414
+ if (batch.length === 0) { brain.setKv(KV_NOISE_DONE, '1'); return 0; }
415
+
374
416
  let stripped = 0;
375
417
  const update = db.transaction(() => {
376
418
  for (const mem of batch) {
377
- const cleaned = stripNoise(mem.content);
378
- if (cleaned !== mem.content) {
419
+ const content = mem.content;
420
+ if (!content || content.length <= 50 || !content.includes('<system-reminder>')) continue;
421
+ const cleaned = stripNoise(content);
422
+ if (cleaned !== content) {
379
423
  db.prepare('UPDATE memories SET content_raw = content, content = ? WHERE id = ?').run(cleaned, mem.id);
380
424
  stripped++;
381
425
  }
@@ -383,26 +427,37 @@ async function noiseStripBatch() {
383
427
  });
384
428
  update();
385
429
  brain.setKv(KV_NOISE_CURSOR, batch[batch.length - 1].id);
430
+ // Short batch ⇒ reached the end of the table ⇒ sweep complete.
431
+ if (batch.length < NOISE_BATCH) brain.setKv(KV_NOISE_DONE, '1');
386
432
  return stripped;
387
433
  }
388
434
 
435
+ // Capped at 21: the only consumers are the done-gate (`<= 20`) and a diagnostic log
436
+ // line. The exact count would read content for every one of 80k+ unclassified rows
437
+ // each tick (full scan); the LIMIT lets SQLite stop after the first 21 matches.
389
438
  function countUnclassified() {
390
439
  try {
391
440
  return brain.getDb().prepare(
392
- "SELECT count(*) as c FROM memories WHERE domain IS NULL AND content IS NOT NULL AND length(content) > 10"
441
+ "SELECT count(*) AS c FROM (SELECT 1 FROM memories WHERE domain IS NULL AND content IS NOT NULL AND length(content) > 10 LIMIT 21)"
393
442
  ).get().c;
394
443
  } catch { return 0; }
395
444
  }
396
445
 
446
+ // Cheap: the accurate anti-join (memories LEFT JOIN embedding_map ... rowid IS NULL)
447
+ // is a full SCAN of memories reading content — tens of seconds cold, every tick.
448
+ // Instead derive remaining work from two bounded counts: the (cached) embeddable
449
+ // total minus the indexed count of memory-type embeddings for the active model.
450
+ // Stale embeddings can make this slightly under-count, which is acceptable: it only
451
+ // gates "is embedding done?" and mirrors the existing `unclassified <= 20` tolerance
452
+ // — a handful of unembedded rows won't meaningfully hurt semantic search.
397
453
  function countUnembedded() {
398
454
  try {
399
455
  const activeModel = embeddings && embeddings.getEmbeddingModel();
400
456
  if (!activeModel) return 0;
401
- return brain.getDb().prepare(`
402
- SELECT count(*) as c FROM memories m
403
- LEFT JOIN embedding_map em ON em.entity_id = m.id AND em.model = ?
404
- WHERE em.rowid IS NULL AND m.content IS NOT NULL AND length(m.content) > 10
405
- `).get(activeModel).c;
457
+ const embedded = brain.getDb().prepare(
458
+ "SELECT count(*) AS c FROM embedding_map WHERE model = ? AND entity_type = 'memory'"
459
+ ).get(activeModel).c;
460
+ return Math.max(0, embeddableMemoryTotal() - embedded);
406
461
  } catch { return 0; }
407
462
  }
408
463
 
@@ -417,6 +472,7 @@ function reset() {
417
472
  brain.setKv(KV_ENTITY_CURSOR, null);
418
473
  brain.setKv(KV_INDEX_CURSOR, null);
419
474
  brain.setKv(KV_NOISE_CURSOR, null);
475
+ brain.setKv(KV_NOISE_DONE, null);
420
476
  }
421
477
 
422
478
  /**
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+ // Deferred, one-shot brain-DB space optimization for users upgrading from a pre-int8 build.
3
+ // Runs OFF the boot path (registered with a multi-minute startDelay) so it never adds to the
4
+ // boot storm, and self-gates to run at most once via a kv flag.
5
+ //
6
+ // Safety model:
7
+ // - SIZE-GATED: the int8 rebuild + FTS rebuild + VACUUM block the (single) event loop for
8
+ // their duration. Tiny for a few-thousand-vector store, ~tens of seconds for hundreds of
9
+ // thousands. So only auto-run when the embedding store is small; otherwise log a one-time
10
+ // notice pointing at the supervised CLI (`scripts/db-optimize/migrate.js`, run with Wall-E
11
+ // stopped) and never nag again.
12
+ // - NEVER drops lost_and_found automatically: that is a per-install SQLite .recover artifact
13
+ // and dropping a recovery table needs explicit human confirmation.
14
+ // - Idempotent + crash-safe: each phase checks if already applied and is transactional, so a
15
+ // restart mid-run simply retries; float32 vectors are never lost (rolled back on crash).
16
+
17
+ const brain = require('../brain');
18
+
19
+ const KV_STATE = 'brain:optimize:v1'; // unset | 'done' | 'notified'
20
+ const AUTO_MAX = parseInt(process.env.BRAIN_OPTIMIZE_AUTO_MAX || '20000', 10);
21
+
22
+ async function runOnce() {
23
+ const state = brain.getKv(KV_STATE);
24
+ if (state === 'done' || state === 'notified') return { done: true, _done: true, skipped: state };
25
+
26
+ let db;
27
+ try { db = brain.getDb(); } catch { return { done: false, reason: 'no-db' }; }
28
+
29
+ // sqlite-vec must be loaded on this connection for the int8 quantization functions.
30
+ try { require('../embeddings').ensureVecLoaded(); } catch {}
31
+
32
+ let mig;
33
+ try { mig = require('../scripts/db-optimize/migrate'); } catch (e) {
34
+ console.error('[brain-optimize] migration module unavailable:', e.message);
35
+ brain.setKv(KV_STATE, 'notified');
36
+ return { done: true, _done: true, skipped: 'module-missing' };
37
+ }
38
+
39
+ if (!mig.needsOptimization(db)) {
40
+ brain.setKv(KV_STATE, 'done');
41
+ return { done: true, _done: true, alreadyOptimal: true };
42
+ }
43
+
44
+ const n = mig.embeddingCount(db);
45
+ if (n > AUTO_MAX) {
46
+ console.warn(
47
+ `[brain-optimize] Brain has ${n} float32 embeddings (> ${AUTO_MAX} auto cap). ` +
48
+ `To reclaim disk, stop Wall-E and run: node wall-e/scripts/db-optimize/migrate.js "${brain.getDbPath()}". ` +
49
+ `Set BRAIN_OPTIMIZE_AUTO_MAX higher to auto-migrate anyway (briefly blocks the event loop).`
50
+ );
51
+ brain.setKv(KV_STATE, 'notified');
52
+ return { done: true, _done: true, skipped: 'large', embeddingCount: n };
53
+ }
54
+
55
+ const t0 = Date.now();
56
+ mig.optimizeBrain(db, {
57
+ vacuum: true,
58
+ dropLostAndFound: false, // recovery artifact — never auto-drop
59
+ log: (...a) => console.log('[brain-optimize]', ...a),
60
+ });
61
+ brain.setKv(KV_STATE, 'done');
62
+ const ms = Date.now() - t0;
63
+ console.log(`[brain-optimize] upgrade migration complete in ${(ms / 1000).toFixed(1)}s (${n} vectors → int8)`);
64
+ return { done: true, _done: true, migrated: true, embeddingCount: n, ms };
65
+ }
66
+
67
+ module.exports = { runOnce, KV_STATE, AUTO_MAX };
@@ -27,17 +27,32 @@ async function runOnce(adapters) {
27
27
 
28
28
  try {
29
29
  const memories = await adapter.poll(since);
30
- for (const mem of memories) {
31
- adapterLatestTimestamp = newerTimestamp(adapterLatestTimestamp, mem.timestamp);
32
- latestTimestamp = newerTimestamp(latestTimestamp, mem.timestamp);
33
- const result = brain.insertMemory(mem);
34
- if (result) {
35
- memoriesIngested++;
36
- adapterIngested++;
37
- if (mem.importance >= 0.8 || mem.memory_type === 'message_received') {
38
- eventBus.emitHighImportance(result?.id, (mem.content || '').slice(0, 200));
30
+ // Insert in chunked transactions instead of N individually-locked INSERTs. One
31
+ // INSERT per message means N cross-process write-lock acquisitions per poll;
32
+ // wrapping ALL of them in a single transaction would swing the other way and hold
33
+ // the lock for the whole batch (the harvest lock-hold class). Chunking bounds the
34
+ // hold while still collapsing N lock acquisitions into N/CHUNK. High-importance
35
+ // events are emitted AFTER each chunk commits, never from inside the transaction.
36
+ const INSERT_CHUNK = 200;
37
+ for (let i = 0; i < memories.length; i += INSERT_CHUNK) {
38
+ const slice = memories.slice(i, i + INSERT_CHUNK);
39
+ const highImportance = [];
40
+ const insertChunk = brain.getDb().transaction(() => {
41
+ for (const mem of slice) {
42
+ adapterLatestTimestamp = newerTimestamp(adapterLatestTimestamp, mem.timestamp);
43
+ latestTimestamp = newerTimestamp(latestTimestamp, mem.timestamp);
44
+ const result = brain.insertMemory(mem);
45
+ if (result) {
46
+ memoriesIngested++;
47
+ adapterIngested++;
48
+ if (mem.importance >= 0.8 || mem.memory_type === 'message_received') {
49
+ highImportance.push({ id: result?.id, content: (mem.content || '').slice(0, 200) });
50
+ }
51
+ }
39
52
  }
40
- }
53
+ });
54
+ insertChunk();
55
+ for (const h of highImportance) eventBus.emitHighImportance(h.id, h.content);
41
56
  }
42
57
  // After inserting individual memories, create exchange pairs for conversational sources
43
58
  if (memories.length >= 2) {
@@ -0,0 +1,160 @@
1
+ 'use strict';
2
+
3
+ // Daily question digest: select the few questions worth the owner's input, render them as a
4
+ // numbered message, and resolve them from the owner's reply. Channel fan-out (Slack/email/
5
+ // CTM tab) and the once-a-day scheduling wrap these pure helpers so they stay testable.
6
+ //
7
+ // Design: docs/superpowers/specs/2026-05-31-question-digest-and-brain-retention-design.md
8
+ // (daily-digest-only; deliver to Slack + email + CTM tab; answer via Slack reply or CTM).
9
+
10
+ const brain = require('../brain');
11
+
12
+ const DEFAULT_MAX = Number(process.env.WALL_E_QUESTION_DIGEST_MAX || 10);
13
+
14
+ function _ownerName() {
15
+ try { return brain.getOwnerName() || 'there'; } catch { return 'there'; }
16
+ }
17
+
18
+ // Render a numbered, priority-tagged digest the owner can answer by replying "1: ... 2: ...".
19
+ function renderDigestText(questions, ownerName = _ownerName()) {
20
+ if (!questions || questions.length === 0) return '';
21
+ const n = questions.length;
22
+ const lines = [
23
+ `Hi ${ownerName} — Wall-E has ${n} question${n === 1 ? '' : 's'} for you:`,
24
+ '',
25
+ ];
26
+ questions.forEach((q, i) => {
27
+ const tag = q.priority && q.priority !== 'normal' ? `[${q.priority}] ` : '';
28
+ lines.push(`${i + 1}. ${tag}${q.question}`);
29
+ });
30
+ lines.push('');
31
+ lines.push('Reply like "1: your answer 2: your answer" — or answer in the Wall-E tab.');
32
+ return lines.join('\n');
33
+ }
34
+
35
+ // Build today's digest from undelivered pending questions. Does NOT mark delivered — the
36
+ // caller marks only after a successful send (so a send failure re-tries tomorrow).
37
+ function buildDigest({ max = DEFAULT_MAX } = {}) {
38
+ const questions = brain.listUndeliveredDigestQuestions({ max });
39
+ return {
40
+ count: questions.length,
41
+ questions,
42
+ text: renderDigestText(questions),
43
+ };
44
+ }
45
+
46
+ // Parse an owner reply that answers numbered questions, e.g.:
47
+ // "1: yes 2 - it's the Dropbox path\n3. no"
48
+ // Maps each leading number to the question at that 1-based position in `questions`.
49
+ // Returns [{ id, answer }]. Unparseable lines are ignored.
50
+ function parseDigestReply(text, questions) {
51
+ if (!text || !Array.isArray(questions) || questions.length === 0) return [];
52
+ const out = [];
53
+ const seen = new Set();
54
+ const re = /(?:^|\n)\s*(\d{1,2})\s*[:.\)\-]\s*(.+?)(?=\n\s*\d{1,2}\s*[:.\)\-]|$)/gs;
55
+ let m;
56
+ while ((m = re.exec(text)) !== null) {
57
+ const idx = parseInt(m[1], 10) - 1;
58
+ const answer = m[2].trim();
59
+ if (idx < 0 || idx >= questions.length || !answer) continue;
60
+ const q = questions[idx];
61
+ if (!q || seen.has(q.id)) continue;
62
+ seen.add(q.id);
63
+ out.push({ id: q.id, answer });
64
+ }
65
+ return out;
66
+ }
67
+
68
+ // Resolve questions from parsed replies. Returns the count resolved.
69
+ function applyDigestReplies(parsed, { resolutionEvidence = 'digest reply' } = {}) {
70
+ if (!Array.isArray(parsed)) return 0;
71
+ let resolved = 0;
72
+ for (const { id, answer } of parsed) {
73
+ try {
74
+ brain.answerQuestion(id, { answer, resolution_type: 'answered', resolution_evidence: resolutionEvidence });
75
+ resolved += 1;
76
+ } catch { /* question may have been resolved/expired already */ }
77
+ }
78
+ return resolved;
79
+ }
80
+
81
+ // ---- Channel fan-out -------------------------------------------------------------------
82
+ // The CTM Wall-E tab is the always-on surface (it lists pending questions + Answer/Dismiss
83
+ // buttons, no opt-in). Slack DM and email are OPT-IN push notifications, off by default,
84
+ // enabled via kv flags (set from the Wall-E settings UI). "Delivered" only gates the push
85
+ // channels — the CTM tab always shows pending questions regardless.
86
+ const KV = {
87
+ slackEnabled: 'digest:slack_enabled',
88
+ emailEnabled: 'digest:email_enabled',
89
+ slackChannel: 'digest:slack_channel',
90
+ emailTo: 'digest:email_to',
91
+ lastSent: 'digest:last_sent_date',
92
+ };
93
+ function _kv(key) { try { return brain.getKv(key) || ''; } catch { return ''; } }
94
+ function _kvBool(key) { return String(_kv(key)).toLowerCase() === 'true'; }
95
+
96
+ function getDigestChannelConfig() {
97
+ return {
98
+ ctm: true, // always on
99
+ slack: _kvBool(KV.slackEnabled),
100
+ slackChannel: _kv(KV.slackChannel),
101
+ email: _kvBool(KV.emailEnabled),
102
+ emailTo: _kv(KV.emailTo),
103
+ };
104
+ }
105
+
106
+ async function _defaultSendSlack(channel, text) {
107
+ const { callSlackMcp } = require('../tools/slack-mcp');
108
+ return callSlackMcp('slack_send_message', { channel, text });
109
+ }
110
+ async function _defaultSendEmail(to, subject, body) {
111
+ const { sendMail } = require('../tools/local-tools');
112
+ return sendMail({ to, subject, body, source: 'wall-e-digest' });
113
+ }
114
+
115
+ // Once-a-day digest job (registered in agent.js). Builds the undelivered digest and pushes
116
+ // it to the OPT-IN channels (Slack/email); marks delivered only if a push actually sent, so
117
+ // enabling a channel later still pushes the backlog once. The CTM tab needs no push.
118
+ // `opts` injects { date, force, sendSlack, sendEmail } for tests.
119
+ async function runDigestJob(opts = {}) {
120
+ const date = opts.date || new Date();
121
+ const todayStr = date.toISOString().slice(0, 10);
122
+ const targetHour = Number(process.env.WALL_E_DIGEST_HOUR || 8);
123
+ if (!opts.force) {
124
+ if (date.getHours() < targetHour) return { sent: false, reason: 'before-target-hour' };
125
+ if (_kv(KV.lastSent) === todayStr) return { sent: false, reason: 'already-sent-today' };
126
+ }
127
+ const cfg = getDigestChannelConfig();
128
+ if (!cfg.slack && !cfg.email) return { sent: false, reason: 'no-push-channels-enabled' };
129
+
130
+ const digest = buildDigest({ max: opts.max || DEFAULT_MAX });
131
+ if (digest.count === 0) return { sent: false, reason: 'no-questions' };
132
+
133
+ const sendSlack = opts.sendSlack || _defaultSendSlack;
134
+ const sendEmail = opts.sendEmail || _defaultSendEmail;
135
+ const results = {};
136
+ let anySent = false;
137
+ if (cfg.slack && cfg.slackChannel) {
138
+ try { await sendSlack(cfg.slackChannel, digest.text); results.slack = 'sent'; anySent = true; }
139
+ catch (e) { results.slack = `err:${e.message}`; }
140
+ } else if (cfg.slack) {
141
+ results.slack = 'skipped:no-channel-configured';
142
+ }
143
+ if (cfg.email && cfg.emailTo) {
144
+ try { await sendEmail(cfg.emailTo, `Wall-E: ${digest.count} question${digest.count === 1 ? '' : 's'} for you`, digest.text); results.email = 'sent'; anySent = true; }
145
+ catch (e) { results.email = `err:${e.message}`; }
146
+ } else if (cfg.email) {
147
+ results.email = 'skipped:no-recipient-configured';
148
+ }
149
+
150
+ if (anySent) {
151
+ brain.markQuestionsDelivered(digest.questions.map((q) => q.id));
152
+ try { brain.setKv(KV.lastSent, todayStr); } catch {}
153
+ }
154
+ return { sent: anySent, count: digest.count, results };
155
+ }
156
+
157
+ module.exports = {
158
+ buildDigest, renderDigestText, parseDigestReply, applyDigestReplies,
159
+ getDigestChannelConfig, runDigestJob, DEFAULT_MAX, KV,
160
+ };
@@ -45,12 +45,14 @@ async function runOnce(opts = {}) {
45
45
  const checkpoint = brain.getCheckpoint('reflect');
46
46
  const lastRunAt = checkpoint ? checkpoint.last_run_at : null;
47
47
 
48
- // Count new memories since last reflect
48
+ // Count new memories since last reflect. Use a DB COUNT — listMemories(...).length
49
+ // pulled every matching row (content blobs and all) just to count them, a full
50
+ // materialization on a 350k-row brain.
49
51
  const sinceOpts = lastRunAt ? { since: lastRunAt } : {};
50
- const memoriesSinceLastReflect = brain.listMemories(sinceOpts).length;
52
+ const memoriesSinceLastReflect = brain.countMemories(sinceOpts);
51
53
 
52
- // Count pending questions
53
- const pendingQuestions = brain.listQuestions({ status: 'pending' }).length;
54
+ // Count pending questions (DB COUNT, not a full materialization)
55
+ const pendingQuestions = brain.countQuestions({ status: 'pending' });
54
56
 
55
57
  // Update checkpoint
56
58
  brain.upsertCheckpoint('reflect', {