create-walle 0.9.21 → 0.9.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/README.md +27 -5
  2. package/package.json +2 -2
  3. package/template/CLAUDE.md +2 -2
  4. package/template/LICENSE +1 -1
  5. package/template/bin/ctm-dev-cleanup.js +24 -3
  6. package/template/bin/ctm-launch.sh +13 -0
  7. package/template/bin/dev.sh +156 -18
  8. package/template/bin/node-bin.sh +84 -0
  9. package/template/bin/pin-node.sh +51 -0
  10. package/template/claude-task-manager/api-prompts.js +1203 -182
  11. package/template/claude-task-manager/api-reviews.js +109 -15
  12. package/template/claude-task-manager/approval-agent.js +1360 -280
  13. package/template/claude-task-manager/bin/restart-ctm.sh +64 -23
  14. package/template/claude-task-manager/bin/storage-migration-supervisor.js +338 -0
  15. package/template/claude-task-manager/db.js +4417 -295
  16. package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
  17. package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
  18. package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
  19. package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
  20. package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
  21. package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
  22. package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
  23. package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
  24. package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
  25. package/template/claude-task-manager/docs/image-paste-ux.md +13 -0
  26. package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
  27. package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
  28. package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
  29. package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
  30. package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
  31. package/template/claude-task-manager/docs/phone-access-design.md +53 -15
  32. package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
  33. package/template/claude-task-manager/docs/phone-setup.md +3 -0
  34. package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
  35. package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
  36. package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
  37. package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
  38. package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
  39. package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
  40. package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
  41. package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
  42. package/template/claude-task-manager/docs/session-title-authority.md +32 -0
  43. package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
  44. package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
  45. package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
  46. package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
  47. package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
  48. package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
  49. package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
  50. package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
  51. package/template/claude-task-manager/git-utils.js +897 -27
  52. package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
  53. package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
  54. package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
  55. package/template/claude-task-manager/lib/agent-presets.js +17 -1
  56. package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
  57. package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
  58. package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
  59. package/template/claude-task-manager/lib/async-semaphore.js +44 -0
  60. package/template/claude-task-manager/lib/auth-context.js +5 -0
  61. package/template/claude-task-manager/lib/auth-rate-limit.js +47 -4
  62. package/template/claude-task-manager/lib/auth-rules.js +29 -2
  63. package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
  64. package/template/claude-task-manager/lib/background-llm.js +144 -17
  65. package/template/claude-task-manager/lib/branch-inventory.js +212 -0
  66. package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
  67. package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
  68. package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
  69. package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
  70. package/template/claude-task-manager/lib/codex-zst.js +124 -0
  71. package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
  72. package/template/claude-task-manager/lib/connection-health.js +232 -0
  73. package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
  74. package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
  75. package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
  76. package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
  77. package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
  78. package/template/claude-task-manager/lib/document-review.js +141 -6
  79. package/template/claude-task-manager/lib/escalation-review.js +152 -0
  80. package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
  81. package/template/claude-task-manager/lib/headless-term-service.js +678 -0
  82. package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
  83. package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
  84. package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
  85. package/template/claude-task-manager/lib/main-db-census.js +216 -0
  86. package/template/claude-task-manager/lib/message-pagination.js +106 -4
  87. package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +750 -26
  88. package/template/claude-task-manager/lib/mobile-auth-api.js +274 -7
  89. package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
  90. package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
  91. package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
  92. package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
  93. package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
  94. package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
  95. package/template/claude-task-manager/lib/perf-tracker.js +242 -6
  96. package/template/claude-task-manager/lib/permission-match.js +76 -0
  97. package/template/claude-task-manager/lib/permission-sync.js +133 -20
  98. package/template/claude-task-manager/lib/process-title.js +35 -0
  99. package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
  100. package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
  101. package/template/claude-task-manager/lib/prompt-intent.js +132 -0
  102. package/template/claude-task-manager/lib/provider-user-context.js +34 -0
  103. package/template/claude-task-manager/lib/read-pool-client.js +313 -0
  104. package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
  105. package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
  106. package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
  107. package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
  108. package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
  109. package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
  110. package/template/claude-task-manager/lib/restart-guard.js +109 -0
  111. package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
  112. package/template/claude-task-manager/lib/restore-policy.js +13 -0
  113. package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
  114. package/template/claude-task-manager/lib/restore-runtime.js +68 -0
  115. package/template/claude-task-manager/lib/restore-storm.js +34 -0
  116. package/template/claude-task-manager/lib/resume-cwd.js +36 -0
  117. package/template/claude-task-manager/lib/resume-preflight.js +313 -0
  118. package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
  119. package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
  120. package/template/claude-task-manager/lib/scheduler.js +21 -1
  121. package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
  122. package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
  123. package/template/claude-task-manager/lib/server-listeners.js +239 -0
  124. package/template/claude-task-manager/lib/session-capture.js +42 -7
  125. package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
  126. package/template/claude-task-manager/lib/session-history.js +388 -43
  127. package/template/claude-task-manager/lib/session-host-manager.js +287 -0
  128. package/template/claude-task-manager/lib/session-image-refs.js +209 -0
  129. package/template/claude-task-manager/lib/session-jobs.js +399 -59
  130. package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
  131. package/template/claude-task-manager/lib/session-restore.js +53 -0
  132. package/template/claude-task-manager/lib/session-standup.js +123 -23
  133. package/template/claude-task-manager/lib/session-state-bus.js +14 -0
  134. package/template/claude-task-manager/lib/session-stream.js +64 -16
  135. package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
  136. package/template/claude-task-manager/lib/session-token-usage.js +494 -0
  137. package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
  138. package/template/claude-task-manager/lib/setup-network-config.js +9 -0
  139. package/template/claude-task-manager/lib/size-cap.js +45 -0
  140. package/template/claude-task-manager/lib/size-cap.test.js +62 -0
  141. package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
  142. package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
  143. package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
  144. package/template/claude-task-manager/lib/standup-attention.js +7 -3
  145. package/template/claude-task-manager/lib/status-authority.js +39 -0
  146. package/template/claude-task-manager/lib/status-hooks.js +4 -0
  147. package/template/claude-task-manager/lib/storage-migration.js +235 -0
  148. package/template/claude-task-manager/lib/structured-capture.js +298 -0
  149. package/template/claude-task-manager/lib/sync-io-census.js +163 -0
  150. package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
  151. package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
  152. package/template/claude-task-manager/lib/terminal-choice.js +364 -0
  153. package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
  154. package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
  155. package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
  156. package/template/claude-task-manager/lib/timeline-order.js +122 -0
  157. package/template/claude-task-manager/lib/transcript-store.js +348 -43
  158. package/template/claude-task-manager/lib/transport-security.js +84 -1
  159. package/template/claude-task-manager/lib/wait-state.js +184 -0
  160. package/template/claude-task-manager/lib/walle-client.js +47 -5
  161. package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
  162. package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
  163. package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
  164. package/template/claude-task-manager/lib/walle-native-health.js +403 -0
  165. package/template/claude-task-manager/lib/walle-repair.js +701 -0
  166. package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
  167. package/template/claude-task-manager/lib/walle-session-context.js +57 -21
  168. package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
  169. package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
  170. package/template/claude-task-manager/lib/walle-transcript.js +52 -0
  171. package/template/claude-task-manager/lib/worktree-active-sync.js +11 -7
  172. package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
  173. package/template/claude-task-manager/package.json +1 -1
  174. package/template/claude-task-manager/prompt-harvest.js +89 -66
  175. package/template/claude-task-manager/providers/claude-code.js +51 -3
  176. package/template/claude-task-manager/providers/cursor.js +140 -45
  177. package/template/claude-task-manager/public/css/reviews.css +551 -61
  178. package/template/claude-task-manager/public/css/setup.css +191 -0
  179. package/template/claude-task-manager/public/css/walle-session.css +865 -10
  180. package/template/claude-task-manager/public/css/walle.css +154 -0
  181. package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
  182. package/template/claude-task-manager/public/index.html +18516 -2058
  183. package/template/claude-task-manager/public/ipad.html +363 -0
  184. package/template/claude-task-manager/public/js/document-review-links.js +301 -0
  185. package/template/claude-task-manager/public/js/image-normalize.js +69 -36
  186. package/template/claude-task-manager/public/js/message-renderer.js +1265 -77
  187. package/template/claude-task-manager/public/js/prompts.js +66 -29
  188. package/template/claude-task-manager/public/js/reviews.js +901 -133
  189. package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
  190. package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
  191. package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
  192. package/template/claude-task-manager/public/js/setup.js +1273 -176
  193. package/template/claude-task-manager/public/js/stream-view.js +691 -73
  194. package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
  195. package/template/claude-task-manager/public/js/walle-session.js +2455 -158
  196. package/template/claude-task-manager/public/js/walle.js +455 -28
  197. package/template/claude-task-manager/public/m/app.css +2909 -262
  198. package/template/claude-task-manager/public/m/app.js +6601 -398
  199. package/template/claude-task-manager/public/m/claim.html +224 -17
  200. package/template/claude-task-manager/public/m/index.html +117 -21
  201. package/template/claude-task-manager/public/m/sw.js +3 -1
  202. package/template/claude-task-manager/public/manifest.json +2 -2
  203. package/template/claude-task-manager/public/prompts.html +30 -14
  204. package/template/claude-task-manager/queue-engine.js +507 -28
  205. package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
  206. package/template/claude-task-manager/server.js +14341 -2197
  207. package/template/claude-task-manager/session-integrity.js +160 -18
  208. package/template/claude-task-manager/session-search-ranking.js +1 -0
  209. package/template/claude-task-manager/session-utils.js +25 -5
  210. package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
  211. package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
  212. package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
  213. package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
  214. package/template/claude-task-manager/workers/harvest-worker.js +9 -55
  215. package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
  216. package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
  217. package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
  218. package/template/claude-task-manager/workers/session-host-process.js +146 -0
  219. package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
  220. package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
  221. package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
  222. package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
  223. package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
  224. package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
  225. package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
  226. package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
  227. package/template/docs/design/markdown-review-pane.md +206 -0
  228. package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +129 -38
  229. package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
  230. package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
  231. package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
  232. package/template/docs/private-memory-and-pii-policy.md +69 -0
  233. package/template/package.json +2 -1
  234. package/template/scripts/check-private-data.js +201 -0
  235. package/template/shared/sqlite-owner-guard.js +30 -0
  236. package/template/shared/sqlite-owner-write-queue.js +225 -0
  237. package/template/shared/sqlite-storage-policy.js +111 -0
  238. package/template/shared/sqlite-write-lock.js +428 -0
  239. package/template/wall-e/agent-runners/claude-code.js +5 -0
  240. package/template/wall-e/agent.js +166 -22
  241. package/template/wall-e/api-walle.js +524 -70
  242. package/template/wall-e/auth/provider-flows.js +11 -1
  243. package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
  244. package/template/wall-e/brain.js +1614 -141
  245. package/template/wall-e/chat/attachment-blocks.js +96 -0
  246. package/template/wall-e/chat/attachments.js +2 -1
  247. package/template/wall-e/chat/capability-resolver.js +7 -7
  248. package/template/wall-e/chat/context-messages.js +28 -0
  249. package/template/wall-e/chat/conversation-frame.js +630 -0
  250. package/template/wall-e/chat/provider-messages.js +125 -0
  251. package/template/wall-e/chat.js +1002 -233
  252. package/template/wall-e/coding/acceptance-contract.js +170 -0
  253. package/template/wall-e/coding/acp-adapter.js +1 -1
  254. package/template/wall-e/coding/agent-catalog.js +3 -0
  255. package/template/wall-e/coding/artifact-store.js +93 -0
  256. package/template/wall-e/coding/capability-router.js +120 -0
  257. package/template/wall-e/coding/coding-run-controller.js +423 -0
  258. package/template/wall-e/coding/compaction-service.js +157 -12
  259. package/template/wall-e/coding/frontend-verification.js +258 -0
  260. package/template/wall-e/coding/lifecycle-hooks.js +75 -0
  261. package/template/wall-e/coding/local-preview-contract.js +157 -0
  262. package/template/wall-e/coding/permission-service.js +57 -13
  263. package/template/wall-e/coding/prompt-bundle.js +19 -1
  264. package/template/wall-e/coding/prompt-section-registry.js +227 -0
  265. package/template/wall-e/coding/provider-compat.js +15 -0
  266. package/template/wall-e/coding/runtime-events.js +224 -0
  267. package/template/wall-e/coding/runtime-mode.js +3 -0
  268. package/template/wall-e/coding/side-git-snapshot.js +160 -4
  269. package/template/wall-e/coding/snapshot-service.js +143 -1
  270. package/template/wall-e/coding/stream-processor.js +388 -34
  271. package/template/wall-e/coding/task-tool.js +141 -4
  272. package/template/wall-e/coding/tool-execution-controller.js +365 -0
  273. package/template/wall-e/coding/tool-registry.js +43 -5
  274. package/template/wall-e/coding/user-hooks.js +217 -0
  275. package/template/wall-e/coding-orchestrator.js +1330 -221
  276. package/template/wall-e/coding-prompts.js +20 -4
  277. package/template/wall-e/context/context-builder.js +15 -2
  278. package/template/wall-e/decision/confidence.js +1 -1
  279. package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
  280. package/template/wall-e/docs/external-action-controller.md +26 -6
  281. package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
  282. package/template/wall-e/embeddings.js +591 -53
  283. package/template/wall-e/external-action-controller.js +12 -0
  284. package/template/wall-e/http/auth.js +1 -0
  285. package/template/wall-e/http/chat-api.js +46 -11
  286. package/template/wall-e/http/model-admin.js +836 -34
  287. package/template/wall-e/lib/boot-profile.js +88 -0
  288. package/template/wall-e/lib/event-loop-monitor.js +93 -0
  289. package/template/wall-e/lib/service-health.js +194 -0
  290. package/template/wall-e/llm/anthropic.js +130 -5
  291. package/template/wall-e/llm/client.js +266 -63
  292. package/template/wall-e/llm/default-fallback.js +382 -0
  293. package/template/wall-e/llm/health.js +19 -0
  294. package/template/wall-e/llm/message-guard.js +78 -0
  295. package/template/wall-e/llm/model-catalog.js +252 -1
  296. package/template/wall-e/llm/openai.js +26 -4
  297. package/template/wall-e/llm/portkey-sync.js +654 -0
  298. package/template/wall-e/llm/provider-error.js +30 -2
  299. package/template/wall-e/llm/registry.js +5 -1
  300. package/template/wall-e/llm/request-compat.js +67 -0
  301. package/template/wall-e/loops/backfill.js +79 -23
  302. package/template/wall-e/loops/brain-optimize.js +67 -0
  303. package/template/wall-e/loops/ingest.js +25 -10
  304. package/template/wall-e/loops/question-digest.js +160 -0
  305. package/template/wall-e/loops/reflect.js +6 -4
  306. package/template/wall-e/loops/think.js +39 -12
  307. package/template/wall-e/mcp-server.js +318 -36
  308. package/template/wall-e/memory/ctm-context-client.js +52 -14
  309. package/template/wall-e/memory/ctm-operational-context.js +237 -0
  310. package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
  311. package/template/wall-e/memory/ctm-session-context.js +111 -63
  312. package/template/wall-e/prompts/coding/deepseek.txt +3 -0
  313. package/template/wall-e/prompts/coding/gemini.txt +6 -0
  314. package/template/wall-e/prompts/coding/gpt.txt +6 -0
  315. package/template/wall-e/prompts/coding/local.txt +7 -0
  316. package/template/wall-e/runtime/decision-hooks.js +115 -0
  317. package/template/wall-e/runtime/devbox-gateway.js +82 -8
  318. package/template/wall-e/runtime/prompt-manifest.js +86 -0
  319. package/template/wall-e/runtime/tool-executor.js +269 -0
  320. package/template/wall-e/runtime/tool-result-envelope.js +138 -0
  321. package/template/wall-e/runtime/transcript-projection.js +60 -0
  322. package/template/wall-e/runtime/walle-runtime.js +224 -0
  323. package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
  324. package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
  325. package/template/wall-e/server.js +15 -0
  326. package/template/wall-e/session-files.js +9 -0
  327. package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
  328. package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
  329. package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
  330. package/template/wall-e/skills/claude-code-reader.js +7 -3
  331. package/template/wall-e/skills/script-skill-runner.js +10 -0
  332. package/template/wall-e/skills/skill-planner.js +38 -0
  333. package/template/wall-e/tools/builtin-middleware.js +19 -9
  334. package/template/wall-e/tools/local-tools.js +1428 -16
  335. package/template/wall-e/tools/permission-checker.js +73 -5
  336. package/template/wall-e/tools/question-manager.js +117 -7
  337. package/template/wall-e/training/harvester.js +12 -28
  338. package/template/wall-e/training/replay.js +25 -80
  339. package/template/website/index.html +10 -10
  340. package/template/wall-e/eval/ab-test.js +0 -203
  341. package/template/wall-e/eval/agent-runner.js +0 -772
  342. package/template/wall-e/eval/agent-scorer.js +0 -461
  343. package/template/wall-e/eval/aggregator.js +0 -414
  344. package/template/wall-e/eval/allowed-test-commands.js +0 -34
  345. package/template/wall-e/eval/benchmark-generator.js +0 -113
  346. package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
  347. package/template/wall-e/eval/benchmarks/chat.json +0 -82
  348. package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
  349. package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
  350. package/template/wall-e/eval/benchmarks/coding.json +0 -122
  351. package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
  352. package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
  353. package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
  354. package/template/wall-e/eval/benchmarks.js +0 -669
  355. package/template/wall-e/eval/cc-replay.js +0 -719
  356. package/template/wall-e/eval/chat-eval.js +0 -525
  357. package/template/wall-e/eval/check-keys.js +0 -15
  358. package/template/wall-e/eval/check-providers.js +0 -42
  359. package/template/wall-e/eval/codex-cli-baseline.js +0 -669
  360. package/template/wall-e/eval/coding-agent-real.js +0 -570
  361. package/template/wall-e/eval/context-compactor.js +0 -251
  362. package/template/wall-e/eval/debug-agent003.js +0 -68
  363. package/template/wall-e/eval/diagnostics.js +0 -216
  364. package/template/wall-e/eval/eval-orchestrator.js +0 -642
  365. package/template/wall-e/eval/evaluate.js +0 -202
  366. package/template/wall-e/eval/evaluator.js +0 -373
  367. package/template/wall-e/eval/exporter.js +0 -212
  368. package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
  369. package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
  370. package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
  371. package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
  372. package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
  373. package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
  374. package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
  375. package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
  376. package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
  377. package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
  378. package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
  379. package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
  380. package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
  381. package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
  382. package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
  383. package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
  384. package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
  385. package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
  386. package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
  387. package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
  388. package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
  389. package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
  390. package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
  391. package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
  392. package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
  393. package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
  394. package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
  395. package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
  396. package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
  397. package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
  398. package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
  399. package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
  400. package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
  401. package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
  402. package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
  403. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
  404. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
  405. package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
  406. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
  407. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
  408. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
  409. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
  410. package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
  411. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
  412. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
  413. package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
  414. package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
  415. package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
  416. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
  417. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
  418. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
  419. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
  420. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
  421. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
  422. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
  423. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
  424. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
  425. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
  426. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
  427. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
  428. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
  429. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
  430. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
  431. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
  432. package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
  433. package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
  434. package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
  435. package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
  436. package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
  437. package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
  438. package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
  439. package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
  440. package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
  441. package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
  442. package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
  443. package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
  444. package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
  445. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
  446. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
  447. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
  448. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
  449. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
  450. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
  451. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
  452. package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
  453. package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
  454. package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
  455. package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
  456. package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
  457. package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
  458. package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
  459. package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
  460. package/template/wall-e/eval/harvester.js +0 -685
  461. package/template/wall-e/eval/head-to-head.js +0 -388
  462. package/template/wall-e/eval/humaneval-adapter.js +0 -321
  463. package/template/wall-e/eval/list-models.js +0 -31
  464. package/template/wall-e/eval/livecodebench-adapter.js +0 -291
  465. package/template/wall-e/eval/mail-integration.js +0 -443
  466. package/template/wall-e/eval/manifest.js +0 -186
  467. package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
  468. package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
  469. package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
  470. package/template/wall-e/eval/meta-harness/cli.js +0 -86
  471. package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
  472. package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
  473. package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
  474. package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
  475. package/template/wall-e/eval/meta-harness/frontier.js +0 -96
  476. package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
  477. package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
  478. package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
  479. package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
  480. package/template/wall-e/eval/meta-harness/reporting.js +0 -58
  481. package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
  482. package/template/wall-e/eval/meta-harness/validation.js +0 -81
  483. package/template/wall-e/eval/promoter.js +0 -228
  484. package/template/wall-e/eval/provider-normalizer.js +0 -33
  485. package/template/wall-e/eval/replay.js +0 -395
  486. package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
  487. package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
  488. package/template/wall-e/eval/run-coding-agent-real.js +0 -187
  489. package/template/wall-e/eval/run-eval.js +0 -435
  490. package/template/wall-e/eval/run-model-comparison.js +0 -142
  491. package/template/wall-e/eval/session-evaluator.js +0 -187
  492. package/template/wall-e/eval/session-miner.js +0 -207
  493. package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
  494. package/template/wall-e/eval/session-transcripts.js +0 -509
  495. package/template/wall-e/eval/shadow.js +0 -161
  496. package/template/wall-e/eval/swebench-adapter.js +0 -345
  497. package/template/wall-e/eval/swebench-docker.js +0 -192
  498. package/template/wall-e/eval/train.py +0 -320
  499. package/template/wall-e/eval/trainer.js +0 -232
  500. package/template/wall-e/eval/weekly-eval-loop.js +0 -241
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ function _isCodexPath(fp) {
7
+ return String(fp || '').includes(`${path.sep}.codex${path.sep}sessions${path.sep}`);
8
+ }
9
+
10
+ // Lossless hydration for the row-store read path.
11
+ //
12
+ // transcript_events rows store a LIGHTWEIGHT, truncated, text-only projection of each
13
+ // message (good for search + fast pagination) plus a byte offset (jsonl_path, byte_start,
14
+ // byte_end) into the source JSONL. To render a session WITHOUT losing content (tool calls,
15
+ // tool results, images, >50KB messages), we use the rows only to LOCATE the page's bytes,
16
+ // then read that bounded span from the JSONL and parse it with the SAME parser the legacy
17
+ // blob/JSONL read path uses (_parseJsonlIntoMessages) — so the hydrated messages are
18
+ // byte-identical to what the blob path renders. The rows are an index; the JSONL stays the
19
+ // source of truth. The read is bounded to the page's span (not the whole multi-GB file), so
20
+ // there is no freeze.
21
+
22
+ function readByteRange(filePath, start, length) {
23
+ const len = Math.max(0, Number(length) || 0);
24
+ if (!filePath || len === 0) return '';
25
+ const fd = fs.openSync(filePath, 'r');
26
+ try {
27
+ const buf = Buffer.alloc(len);
28
+ const n = fs.readSync(fd, buf, 0, len, Math.max(0, Number(start) || 0));
29
+ return buf.subarray(0, n).toString('utf8');
30
+ } finally {
31
+ try { fs.closeSync(fd); } catch { /* ignore */ }
32
+ }
33
+ }
34
+
35
+ // rows: page rows with { jsonl_path, byte_start, byte_end }.
36
+ // lightweightFallback: the row-text messages to serve if NOTHING can be hydrated
37
+ // (every source file moved/truncated) — so a missing file degrades to text, never throws.
38
+ // Returns FULL-fidelity messages for the page, chronologically sorted.
39
+ function hydratePageMessages(rows, lightweightFallback = []) {
40
+ let parser;
41
+ try { parser = require('./jsonl-conversation-parser'); } catch { return lightweightFallback; }
42
+ const claudeParse = parser._parseJsonlIntoMessages;
43
+ if (typeof claudeParse !== 'function') return lightweightFallback;
44
+ // The Codex content parser lives in session-history (jsonl-conversation-parser imports but
45
+ // does NOT re-export it). Without it, Codex content parses as 0/garbage.
46
+ let codexParse = null;
47
+ try {
48
+ const sh = require('./session-history');
49
+ if (typeof sh.parseCodexJsonlIntoMessages === 'function') codexParse = sh.parseCodexJsonlIntoMessages;
50
+ } catch { /* optional */ }
51
+
52
+ const list = Array.isArray(rows) ? rows : [];
53
+ if (list.length === 0) return lightweightFallback;
54
+ const liteOf = (r) => ({ role: r && r.role, text: (r && (r.text != null ? r.text : r.content)) || '', timestamp: (r && r.timestamp) || '' });
55
+
56
+ // Group the page's rows per source file and decide PER FILE how to hydrate:
57
+ // - Contiguous span (Claude: timestamps monotonic with byte → the page's rows sit in a
58
+ // small byte range). Reading that whole span and parsing it captures the tool-use /
59
+ // tool-result lines BETWEEN the message rows — which the transcript rows omit but the
60
+ // blob/JSONL render shows. This is what makes Claude render-identical to the blob path.
61
+ // - Row-by-row (Codex: timestamps are NOT monotonic with byte, so the page's rows scatter
62
+ // across the whole multi-GB file; a span read would cover ~the entire file = wrong content
63
+ // + a near-freeze). Reading each row's own line stays bounded and exact.
64
+ // The span-size threshold distinguishes the two automatically from the actual byte layout.
65
+ const MAX_SPAN_BYTES = 16 * 1024 * 1024;
66
+ const byFile = new Map();
67
+ for (const r of list) {
68
+ const fp = r && r.jsonl_path;
69
+ if (!fp || !(Number(r.byte_end) > Number(r.byte_start))) { byFile.set('__lite__:' + (byFile.size), { lite: liteOf(r) }); continue; }
70
+ const g = byFile.get(fp) || { fp, rows: [], min: Infinity, max: 0 };
71
+ g.rows.push(r); g.min = Math.min(g.min, Number(r.byte_start)); g.max = Math.max(g.max, Number(r.byte_end));
72
+ byFile.set(fp, g);
73
+ }
74
+
75
+ const out = [];
76
+ let hydratedAny = false;
77
+ const parseInto = (content, fp, sink) => {
78
+ if (_isCodexPath(fp) && typeof codexParse === 'function') codexParse(content, sink, {});
79
+ else claudeParse(content, sink, { sourceFile: fp });
80
+ };
81
+ for (const [, g] of byFile) {
82
+ if (g.lite) { out.push(g.lite); continue; }
83
+ const spanBytes = g.max - g.min;
84
+ // Span-read only for Claude/generic (rows contiguous in byte; captures interleaved tool
85
+ // lines). Codex ALWAYS goes row-by-row: its rows already exclude tool/event payloads, and
86
+ // its timestamps aren't byte-monotonic so even a small span can mis-span. Provider, not
87
+ // span size, is the right discriminator (small-span Codex regressed under a size-only rule).
88
+ if (!_isCodexPath(g.fp) && spanBytes > 0 && spanBytes <= MAX_SPAN_BYTES) {
89
+ let content = '';
90
+ try { content = readByteRange(g.fp, g.min, spanBytes); } catch { content = ''; }
91
+ const parsed = [];
92
+ if (content) { try { parseInto(content, g.fp, parsed); } catch { /* */ } }
93
+ if (parsed.length) { for (const m of parsed) out.push(m); hydratedAny = true; continue; }
94
+ // fall through to row-by-row if the span produced nothing
95
+ }
96
+ for (const r of g.rows) {
97
+ let content = '';
98
+ try { content = readByteRange(g.fp, Number(r.byte_start), Number(r.byte_end) - Number(r.byte_start)); } catch { content = ''; }
99
+ const parsed = [];
100
+ if (content) { try { parseInto(content, g.fp, parsed); } catch { /* */ } }
101
+ if (parsed.length) { for (const m of parsed) out.push(m); hydratedAny = true; }
102
+ else out.push(liteOf(r));
103
+ }
104
+ }
105
+ if (!hydratedAny) return lightweightFallback;
106
+ out.forEach(m => { if (m) { delete m._sourceFile; delete m._sourceLine; delete m._entrySessionId; } });
107
+ // Order chronologically to match the blob path's sortTimelineMessagesInPlace.
108
+ out.sort((a, b) => String(a.timestamp || '').localeCompare(String(b.timestamp || '')));
109
+ return out;
110
+ }
111
+
112
+ module.exports = { hydratePageMessages, readByteRange };
@@ -0,0 +1,216 @@
1
+ 'use strict';
2
+
3
+ // Main-thread DB census + budget guard.
4
+ //
5
+ // THE PROBLEM IT SOLVES: CTM's freeze is never really "addApprovalObservation" or
6
+ // "backfillRewrite" — those are just WHICH of the dozens of synchronous main-thread DB
7
+ // callers happened to hit cold Dropbox pages first this boot. The existing
8
+ // [perf-lag]/[freeze-probe] diagnostics surface the single worst block, so we fix one
9
+ // driver, the next surfaces, forever (whack-a-mole). This module turns the hunt into a
10
+ // CENSUS: instrument the main-thread DB handle so a single cold boot yields the COMPLETE
11
+ // ranked list of every main-thread DB op (by cumulative + max cost, with a stack for the
12
+ // slow ones) — and a definition of done: zero UNBOUNDED main-thread ops on the cold path.
13
+ //
14
+ // THE INVARIANT IT ENFORCES: "no unbounded synchronous DB op on the main thread." A
15
+ // single-row indexed .get() is fine even cold (one page); a full scan, a SELECT * on a
16
+ // blob column, or a multi-row write freezes the loop. When an op exceeds the budget we
17
+ // log `[db-budget]` with the SQL + stack so a NEW violation shows up the moment it's
18
+ // written, not three restarts later.
19
+ //
20
+ // SAFETY: this is debug instrumentation, OFF by default. When disabled, maybeInstrument()
21
+ // returns the raw better-sqlite3 handle untouched — zero overhead, zero behavior change.
22
+ // HARD RULE: it adds per-DB-call overhead, so it must NEVER run on the primary (doing so
23
+ // caused a real production slowdown). It only arms on a dev/staging instance (which sets
24
+ // DEV_CTM_PORT) with CTM_DB_CENSUS=1. There is no primary-arming path.
25
+
26
+
27
+ // Capture a stack for an op only when it is at least this slow (ms) — keeps per-call
28
+ // overhead negligible for the thousands of fast ops while still attributing every freeze.
29
+ const STACK_MS = clampInt(process.env.CTM_DB_CENSUS_STACK_MS, 25, 1, 60000);
30
+ // Emit a `[db-budget]` warning when a single main-thread op exceeds this (ms). This is the
31
+ // invariant tripwire — an unbounded op (scan / blob / big write) on the loop.
32
+ const BUDGET_MS = clampInt(process.env.CTM_DB_CENSUS_BUDGET_MS, 100, 1, 600000);
33
+ // Cap distinct SQL keys retained, so a pathological dynamic-SQL generator can't grow the
34
+ // census map without bound. The hottest keys (by sumMs) are always kept.
35
+ const MAX_KEYS = clampInt(process.env.CTM_DB_CENSUS_MAX_KEYS, 2000, 16, 100000);
36
+
37
+ function clampInt(raw, dflt, lo, hi) {
38
+ const n = Number(raw);
39
+ if (!Number.isFinite(n)) return dflt;
40
+ return Math.max(lo, Math.min(hi, Math.trunc(n)));
41
+ }
42
+
43
+ let _enabled = false;
44
+ let _armedReason = '';
45
+ const _census = new Map(); // sqlKey → stats
46
+ let _startedAt = 0;
47
+ let _budgetWarnings = 0;
48
+
49
+ // Decide whether to arm. HARD RULE: this census wraps the live DB handle and adds per-call
50
+ // overhead — it must NEVER run on the primary (it caused a real production slowdown there).
51
+ // It only arms on a dev/staging instance, which sets DEV_CTM_PORT. There is deliberately no
52
+ // primary-arming path (no sentinel) — staging only.
53
+ function _shouldArm() {
54
+ if (!process.env.DEV_CTM_PORT) return ''; // primary: never arm, full stop
55
+ if (process.env.CTM_DB_CENSUS === '1') return 'env';
56
+ return '';
57
+ }
58
+
59
+ function isEnabled() { return _enabled; }
60
+ function armedReason() { return _armedReason; }
61
+
62
+ function _shortSql(sql) {
63
+ const s = String(sql || '').replace(/\s+/g, ' ').trim();
64
+ return s.length > 200 ? s.slice(0, 200) + '…' : s;
65
+ }
66
+
67
+ // A trimmed stack: drop this module's frames + the node internals, keep the CTM call site.
68
+ function _captureStack() {
69
+ const raw = new Error().stack || '';
70
+ return raw
71
+ .split('\n')
72
+ .slice(1)
73
+ .filter((l) => !l.includes('main-db-census.js') && !l.includes('node:internal'))
74
+ .slice(0, 6)
75
+ .map((l) => l.trim())
76
+ .join(' <- ');
77
+ }
78
+
79
+ function _record(sqlKey, op, ms, rows) {
80
+ let st = _census.get(sqlKey);
81
+ if (!st) {
82
+ if (_census.size >= MAX_KEYS) return; // bounded; hottest keys already retained
83
+ st = { sql: _shortSql(sqlKey), op, count: 0, sumMs: 0, maxMs: 0, maxRows: 0, slowStack: '', slowMs: 0 };
84
+ _census.set(sqlKey, st);
85
+ }
86
+ st.count += 1;
87
+ st.sumMs += ms;
88
+ if (ms > st.maxMs) st.maxMs = ms;
89
+ if (rows > st.maxRows) st.maxRows = rows;
90
+ if (ms >= STACK_MS && ms > st.slowMs) {
91
+ st.slowMs = ms;
92
+ st.slowStack = _captureStack();
93
+ }
94
+ if (ms >= BUDGET_MS) {
95
+ _budgetWarnings += 1;
96
+ // The invariant tripwire. Logged (not thrown) in prod so it can never break a request;
97
+ // a dev harness can choose to throw on these.
98
+ console.warn(`[db-budget] main-thread ${op} took ${Math.round(ms)}ms rows=${rows} sql="${st.sql}"`
99
+ + (st.slowStack ? `\n ${st.slowStack}` : ''));
100
+ }
101
+ }
102
+
103
+ // Wrap a better-sqlite3 Statement with timed get/all/run/iterate. Toggle/chaining methods
104
+ // (pluck/expand/raw/bind/safeIntegers) return `this` in better-sqlite3, so our versions
105
+ // return the WRAPPER to keep the proxy across chains (e.g. `.pluck().get()` stays timed).
106
+ function _wrapStatement(stmt) {
107
+ const sqlKey = stmt.source || '(unknown sql)';
108
+ const timed = (op) => function (...args) {
109
+ const t0 = process.hrtime.bigint();
110
+ const out = stmt[op](...args);
111
+ const ms = Number(process.hrtime.bigint() - t0) / 1e6;
112
+ let rows = 0;
113
+ if (op === 'all' && Array.isArray(out)) rows = out.length;
114
+ else if (op === 'get') rows = out ? 1 : 0;
115
+ else if (op === 'run' && out && typeof out.changes === 'number') rows = out.changes;
116
+ _record(sqlKey, op, ms, rows);
117
+ return out;
118
+ };
119
+ const wrapper = {
120
+ get: timed('get'),
121
+ all: timed('all'),
122
+ run: timed('run'),
123
+ iterate: timed('iterate'),
124
+ };
125
+ // Chaining toggles → delegate, but return the wrapper so timing survives the chain.
126
+ for (const m of ['pluck', 'expand', 'raw', 'bind', 'safeIntegers']) {
127
+ if (typeof stmt[m] === 'function') {
128
+ wrapper[m] = function (...args) { stmt[m](...args); return wrapper; };
129
+ }
130
+ }
131
+ // Pass-through for metadata methods/props that callers read.
132
+ wrapper.columns = function (...args) { return stmt.columns(...args); };
133
+ Object.defineProperties(wrapper, {
134
+ source: { get: () => stmt.source },
135
+ reader: { get: () => stmt.reader },
136
+ readonly: { get: () => stmt.readonly },
137
+ busy: { get: () => stmt.busy },
138
+ database: { get: () => stmt.database },
139
+ _rawStatement: { value: stmt },
140
+ });
141
+ return wrapper;
142
+ }
143
+
144
+ // Wrap the connection so .prepare() returns wrapped statements and .exec/.pragma are timed.
145
+ // Everything else delegates to the raw handle unchanged (transaction, backup, pragma config,
146
+ // close, etc.), so behavior is identical — only the timed paths are observed.
147
+ function _wrapConnection(rawDb) {
148
+ return new Proxy(rawDb, {
149
+ get(target, prop, receiver) {
150
+ if (prop === 'prepare') {
151
+ return function (sql) {
152
+ const stmt = target.prepare(sql);
153
+ try { return _wrapStatement(stmt); } catch { return stmt; }
154
+ };
155
+ }
156
+ if (prop === 'exec') {
157
+ return function (sql) {
158
+ const t0 = process.hrtime.bigint();
159
+ const out = target.exec(sql);
160
+ const ms = Number(process.hrtime.bigint() - t0) / 1e6;
161
+ _record(`exec: ${_shortSql(sql)}`, 'exec', ms, 0);
162
+ return out;
163
+ };
164
+ }
165
+ if (prop === 'pragma') {
166
+ return function (source, options) {
167
+ const t0 = process.hrtime.bigint();
168
+ const out = target.pragma(source, options);
169
+ const ms = Number(process.hrtime.bigint() - t0) / 1e6;
170
+ _record(`pragma: ${_shortSql(source)}`, 'pragma', ms, 0);
171
+ return out;
172
+ };
173
+ }
174
+ const val = Reflect.get(target, prop, receiver);
175
+ return typeof val === 'function' ? val.bind(target) : val;
176
+ },
177
+ });
178
+ }
179
+
180
+ // Wrap the handle IF armed; otherwise return it untouched. Called once per initDb/connectDb
181
+ // right after the connection is opened, BEFORE the restore storm runs.
182
+ function maybeInstrument(rawDb) {
183
+ if (!rawDb) return rawDb;
184
+ if (!_enabled) {
185
+ const reason = _shouldArm();
186
+ if (!reason) return rawDb; // default path: raw handle, zero overhead
187
+ _enabled = true;
188
+ _armedReason = reason;
189
+ _startedAt = Date.now();
190
+ console.warn(`[db-census] ARMED (${reason}) — instrumenting main-thread DB handle; `
191
+ + `budget=${BUDGET_MS}ms stackThreshold=${STACK_MS}ms. GET /api/diagnostics/db-census to read.`);
192
+ }
193
+ try { return _wrapConnection(rawDb); } catch { return rawDb; }
194
+ }
195
+
196
+ // Ranked snapshot (by cumulative main-thread ms — the cold-boot cost of each query).
197
+ function getCensus({ limit = 100 } = {}) {
198
+ const rows = [..._census.values()]
199
+ .map((s) => ({ ...s, avgMs: s.count ? +(s.sumMs / s.count).toFixed(2) : 0,
200
+ sumMs: +s.sumMs.toFixed(1), maxMs: +s.maxMs.toFixed(1) }))
201
+ .sort((a, b) => b.sumMs - a.sumMs);
202
+ return {
203
+ enabled: _enabled,
204
+ armedReason: _armedReason,
205
+ sinceMs: _startedAt ? Date.now() - _startedAt : 0,
206
+ budgetMs: BUDGET_MS,
207
+ stackThresholdMs: STACK_MS,
208
+ budgetWarnings: _budgetWarnings,
209
+ distinctSql: _census.size,
210
+ top: rows.slice(0, Math.max(1, limit)),
211
+ };
212
+ }
213
+
214
+ function reset() { _census.clear(); _budgetWarnings = 0; _startedAt = Date.now(); }
215
+
216
+ module.exports = { maybeInstrument, getCensus, reset, isEnabled, armedReason };
@@ -17,6 +17,15 @@
17
17
  const MIN_PAGE_SIZE = 1;
18
18
  const MAX_PAGE_SIZE = 1000;
19
19
 
20
+ function _pageSize(limit) {
21
+ const rawLimit = Number.isFinite(limit) ? Math.floor(limit) : 0;
22
+ return Math.max(MIN_PAGE_SIZE, Math.min(MAX_PAGE_SIZE, rawLimit || MIN_PAGE_SIZE));
23
+ }
24
+
25
+ function _loadedOffset(offset) {
26
+ return Math.max(0, Number.isFinite(offset) ? Math.floor(offset) : 0);
27
+ }
28
+
20
29
  /**
21
30
  * @param {Array} all — chronologically sorted messages
22
31
  * @param {number} offset — how many most-recent messages are already loaded
@@ -29,9 +38,8 @@ function paginateMessages(all, offset, limit) {
29
38
  if (total === 0) {
30
39
  return { messages: [], total: 0, has_more: false, next_offset: 0 };
31
40
  }
32
- const alreadyLoaded = Math.max(0, Number.isFinite(offset) ? Math.floor(offset) : 0);
33
- const rawLimit = Number.isFinite(limit) ? Math.floor(limit) : 0;
34
- const pageSize = Math.max(MIN_PAGE_SIZE, Math.min(MAX_PAGE_SIZE, rawLimit || MIN_PAGE_SIZE));
41
+ const alreadyLoaded = _loadedOffset(offset);
42
+ const pageSize = _pageSize(limit);
35
43
  const remaining = Math.max(0, total - alreadyLoaded);
36
44
  const take = Math.min(pageSize, remaining);
37
45
  const endIdx = total - alreadyLoaded; // exclusive
@@ -44,4 +52,98 @@ function paginateMessages(all, offset, limit) {
44
52
  };
45
53
  }
46
54
 
47
- module.exports = { paginateMessages, MIN_PAGE_SIZE, MAX_PAGE_SIZE };
55
+ function defaultIsTurnStart(message) {
56
+ return !!message
57
+ && String(message.role || '').trim() === 'user'
58
+ && String(message.text || message.content || message.message || '').trim().length > 0;
59
+ }
60
+
61
+ function splitMessagesIntoTurns(all, options = {}) {
62
+ const arr = Array.isArray(all) ? all : [];
63
+ const isTurnStart = typeof options.isTurnStart === 'function'
64
+ ? options.isTurnStart
65
+ : defaultIsTurnStart;
66
+ const setup = [];
67
+ const turns = [];
68
+ let current = null;
69
+
70
+ arr.forEach((message, index) => {
71
+ if (isTurnStart(message, index, arr)) {
72
+ current = {
73
+ type: 'turn',
74
+ start_index: index,
75
+ end_index: index,
76
+ messages: [message],
77
+ };
78
+ turns.push(current);
79
+ return;
80
+ }
81
+ if (current) {
82
+ current.messages.push(message);
83
+ current.end_index = index;
84
+ return;
85
+ }
86
+ setup.push(message);
87
+ });
88
+
89
+ if (setup.length > 0) {
90
+ turns.unshift({
91
+ type: 'setup',
92
+ start_index: 0,
93
+ end_index: setup.length - 1,
94
+ messages: setup,
95
+ });
96
+ }
97
+ return turns;
98
+ }
99
+
100
+ /**
101
+ * Paginates by complete semantic turns instead of raw rows.
102
+ *
103
+ * Review/conversation UIs group raw provider rows after fetching. Raw-message
104
+ * pagination can begin in the middle of a long tool-heavy turn, which leaves
105
+ * the renderer without the owning user prompt and makes it collapse everything
106
+ * into a fake "setup" block. This helper keeps the same newest-first pagination
107
+ * shape as paginateMessages, but `offset` and `limit` count complete turns.
108
+ */
109
+ function paginateMessageTurns(all, offset, limit, options = {}) {
110
+ const turns = splitMessagesIntoTurns(all, options);
111
+ const total = turns.length;
112
+ if (total === 0) {
113
+ return {
114
+ messages: [],
115
+ total: 0,
116
+ has_more: false,
117
+ next_offset: 0,
118
+ page_kind: 'turns',
119
+ turn_count: 0,
120
+ };
121
+ }
122
+
123
+ const alreadyLoaded = _loadedOffset(offset);
124
+ const pageSize = _pageSize(limit);
125
+ const remaining = Math.max(0, total - alreadyLoaded);
126
+ const take = Math.min(pageSize, remaining);
127
+ const endIdx = total - alreadyLoaded;
128
+ const startIdx = Math.max(0, endIdx - take);
129
+ const pageTurns = turns.slice(startIdx, endIdx);
130
+
131
+ return {
132
+ messages: pageTurns.flatMap(turn => turn.messages),
133
+ total,
134
+ has_more: startIdx > 0,
135
+ next_offset: alreadyLoaded + take,
136
+ page_kind: 'turns',
137
+ turn_count: pageTurns.length,
138
+ first_turn_index: pageTurns.length ? startIdx : -1,
139
+ last_turn_index: pageTurns.length ? endIdx - 1 : -1,
140
+ };
141
+ }
142
+
143
+ module.exports = {
144
+ paginateMessages,
145
+ paginateMessageTurns,
146
+ splitMessagesIntoTurns,
147
+ MIN_PAGE_SIZE,
148
+ MAX_PAGE_SIZE,
149
+ };