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,38 @@
1
+ 'use strict';
2
+
3
+ // Fallback policy for a PERIODIC, HEAVY, idempotent db-owner-worker job (a backfill or
4
+ // import sweep) when its worker op fails. The hard rule this enforces: a slow/degraded/
5
+ // timed-out worker must NEVER cause the heavy sweep to run on the main event loop — that
6
+ // turns transient worker saturation into a multi-second UI freeze (a giant transcript
7
+ // ingest holds the SQLite write lock → the backfill op times out → its .catch() ran the
8
+ // whole JSON.parse + row-rewrite on main). These jobs are idempotent and re-run on the
9
+ // next scheduler tick once the worker recovers, so "skip this tick" is correct.
10
+ //
11
+ // The ONE case where the in-process owner queue IS the intended path is a genuinely
12
+ // DISABLED worker (feature off by config, CTM_DB_OWNER_WORKER_DISABLED): then there is no
13
+ // worker to contend with and in-process is how writes are meant to run.
14
+ //
15
+ // Pure + dependency-injected (runInProcess, logDropped) so it is unit-testable in isolation
16
+ // from server.js. Mirrors the _runBestEffortWorkerOpOrDrop policy but returns a skip result
17
+ // the scheduler tolerates instead of a fire-and-forget drop.
18
+
19
+ const WORKER_DISABLED_CODE = 'CTM_DB_OWNER_WORKER_DISABLED';
20
+
21
+ function makeSkipHeavyWorkerJobOnDegraded({ runInProcess, logDropped } = {}) {
22
+ if (typeof runInProcess !== 'function') {
23
+ throw new Error('makeSkipHeavyWorkerJobOnDegraded requires a runInProcess function');
24
+ }
25
+ // Returns a factory: (label, skipResult, inProcessFn) → a .catch() handler.
26
+ // skipResult may be a value or a thunk (evaluated lazily on the skip path).
27
+ return function skipHeavyWorkerJobOnDegraded(label, skipResult, inProcessFn) {
28
+ return (workerError) => {
29
+ if (workerError && workerError.code === WORKER_DISABLED_CODE) {
30
+ return runInProcess(label, inProcessFn);
31
+ }
32
+ if (typeof logDropped === 'function') logDropped(label, workerError);
33
+ return Promise.resolve(typeof skipResult === 'function' ? skipResult() : skipResult);
34
+ };
35
+ };
36
+ }
37
+
38
+ module.exports = { makeSkipHeavyWorkerJobOnDegraded, WORKER_DISABLED_CODE };
@@ -0,0 +1,542 @@
1
+ 'use strict';
2
+
3
+ // Shared JSONL → conversation-message parser.
4
+ //
5
+ // Extracted VERBATIM from server.js so the same parser can run on the main
6
+ // thread AND inside the read-pool worker (workers/read-pool-worker.js) without
7
+ // forking behavior. The Conversation tab and the #review= page both render the
8
+ // output of this parser via public/js/message-renderer.js, so any divergence
9
+ // here would break Conversation/Review render parity — the functions below must
10
+ // stay byte-for-byte identical to what server.js used. server.js now re-exports
11
+ // these (alias) instead of defining its own copies.
12
+ //
13
+ // Dependencies are already standalone modules (no server.js coupling):
14
+ // - parseCodexJsonlIntoMessages / parseCodexJsonlFileIntoMessages /
15
+ // createCodexUserDeduper (lib/session-history) — Codex rollout format
16
+ // - isProviderGeneratedUserContextText (lib/provider-user-context)
17
+ // - sortTimelineMessagesInPlace (lib/timeline-order)
18
+ // - node:fs, node:path
19
+
20
+ const fs = require('node:fs');
21
+ const path = require('node:path');
22
+ const {
23
+ parseCodexJsonlIntoMessages,
24
+ parseCodexJsonlFileIntoMessages,
25
+ createCodexUserDeduper,
26
+ } = require('./session-history');
27
+ const { isZstRolloutFileName, materializeCodexRollout } = require('./codex-zst');
28
+ const { isProviderGeneratedUserContextText } = require('./provider-user-context');
29
+ const { sortTimelineMessagesInPlace } = require('./timeline-order');
30
+ const {
31
+ structuredCaptureEnabled,
32
+ compactBoundaryMessage,
33
+ compactSummaryMessage,
34
+ reasoningMessage,
35
+ toolCallMessage,
36
+ toolResultMessage,
37
+ } = require('./structured-capture');
38
+
39
+ // Usage fields worth persisting on assistant messages (token chips).
40
+ function _usageMetadata(usage) {
41
+ if (!usage || typeof usage !== 'object') return null;
42
+ const out = {};
43
+ for (const key of ['input_tokens', 'output_tokens', 'cache_creation_input_tokens', 'cache_read_input_tokens']) {
44
+ const value = Number(usage[key]);
45
+ if (Number.isFinite(value) && value >= 0) out[key] = value;
46
+ }
47
+ return Object.keys(out).length ? out : null;
48
+ }
49
+
50
+ const _SESSION_FILE_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
51
+
52
+ // Session id a Claude JSONL file OWNS, derived from its filename stem
53
+ // (`<uuid>.jsonl` / `<uuid>.jsonl.bak`). '' when the stem isn't a UUID
54
+ // (codex rollouts, walle history files).
55
+ function claudeFileSessionId(filePath) {
56
+ const stem = path.basename(String(filePath || '')).replace(/\.jsonl(\.bak)?$/i, '');
57
+ return _SESSION_FILE_UUID_RE.test(stem) ? stem : '';
58
+ }
59
+
60
+ // Byte budgets for the multi-file parse. Kept here (not server.js) so the read
61
+ // pool applies the identical thresholds. Values match the originals in server.js.
62
+ const LARGE_SESSION_TAIL_THRESHOLD_BYTES = 50 * 1024 * 1024;
63
+ const LARGE_SESSION_TAIL_READ_BYTES = 10 * 1024 * 1024;
64
+ const LATENCY_SENSITIVE_SYNC_BUDGET_BYTES = 12 * 1024 * 1024;
65
+ const PARSE_MAX_SYNC_SIZE = 10 * 1024 * 1024; // 10MB total budget for non-latency parse
66
+ const PARSE_SMALL_FILE_THRESHOLD = 1024 * 1024; // always read small (subagent) files
67
+
68
+ function _jsonlContentText(content) {
69
+ if (typeof content === 'string') return content;
70
+ if (!Array.isArray(content)) return '';
71
+ const parts = [];
72
+ let imageCount = 0;
73
+ for (const block of content) {
74
+ if (!block || typeof block !== 'object') continue;
75
+ if ((block.type === 'text' || block.type === 'input_text') && block.text) {
76
+ parts.push(block.text);
77
+ } else if (block.type === 'image' || block.type === 'input_image' || block.type === 'image_ref') {
78
+ parts.push(`[Image #${++imageCount}]`);
79
+ }
80
+ }
81
+ return parts.join('\n');
82
+ }
83
+
84
+ function _isClaudeSubagentJsonlPath(filePath) {
85
+ return String(filePath || '').replace(/\\/g, '/').includes('/subagents/');
86
+ }
87
+
88
+ // Subagent linkage: `subagents/agent-<id>.jsonl` has a sibling
89
+ // `agent-<id>.meta.json` carrying {agentType, description, toolUseId} —
90
+ // toolUseId equals the spawning Task tool_use's id (the tool_call row's
91
+ // callId), so nesting needs no prompt-matching. Cached per file path.
92
+ const _subagentFileInfoCache = new Map();
93
+ function _readSubagentFileInfo(filePath) {
94
+ const key = String(filePath || '');
95
+ if (_subagentFileInfoCache.has(key)) return _subagentFileInfoCache.get(key);
96
+ const idMatch = path.basename(key).match(/^agent-(.+)\.jsonl$/i);
97
+ const info = { id: idMatch ? idMatch[1] : '', agentType: '', description: '', parentToolUseId: '' };
98
+ if (idMatch) {
99
+ try {
100
+ const meta = JSON.parse(fs.readFileSync(key.replace(/\.jsonl$/i, '.meta.json'), 'utf8'));
101
+ if (meta && typeof meta === 'object') {
102
+ info.agentType = typeof meta.agentType === 'string' ? meta.agentType : '';
103
+ info.description = typeof meta.description === 'string' ? meta.description.slice(0, 200) : '';
104
+ info.parentToolUseId = typeof meta.toolUseId === 'string' ? meta.toolUseId : '';
105
+ }
106
+ } catch { /* meta.json missing — linkage falls back to render-time matching */ }
107
+ }
108
+ if (_subagentFileInfoCache.size > 512) _subagentFileInfoCache.clear();
109
+ _subagentFileInfoCache.set(key, info);
110
+ return info;
111
+ }
112
+
113
+ function _parseJsonlIntoMessages(content, messages, opts = {}) {
114
+ if (content.includes('"type":"session_meta"') && content.includes('"originator":"codex-tui"')) {
115
+ parseCodexJsonlIntoMessages(content, messages);
116
+ return;
117
+ }
118
+ const sourceFile = opts && opts.sourceFile ? path.resolve(opts.sourceFile) : '';
119
+ const sourceIsSubagent = _isClaudeSubagentJsonlPath(sourceFile);
120
+ const subagentFileInfo = sourceIsSubagent ? _readSubagentFileInfo(sourceFile) : null;
121
+ const summaries = opts && Array.isArray(opts.summaries) ? opts.summaries : null;
122
+ // Line-level sessionId of the entry currently being parsed. Stamped on each
123
+ // message so parseSessionFiles can drop inherited post-compact prefixes
124
+ // (a new Claude session file starts with the PARENT file's lines, old
125
+ // sessionId at the top, switching mid-file).
126
+ let entrySessionId = '';
127
+ // Per-line sidechain identity (inline Task-subagent turns carry
128
+ // isSidechain/agentId/slug on the entry itself).
129
+ let entrySidechain = null;
130
+ let entrySlug = '';
131
+ // Last contiguous run of messages emitted for one assistant parentUuid
132
+ // (structured emission replaces the whole run when the row re-streams).
133
+ let lastAssistantRun = null;
134
+ const pushMessage = (msg, lineIdx) => {
135
+ if (sourceFile) {
136
+ msg._sourceFile = sourceFile;
137
+ msg._sourceLine = lineIdx;
138
+ }
139
+ if (entrySessionId) msg._entrySessionId = entrySessionId;
140
+ if (sourceIsSubagent || entrySidechain) {
141
+ const metadata = msg.metadata && typeof msg.metadata === 'object' ? msg.metadata : {};
142
+ msg.metadata = { ...metadata, sourceKind: 'subagent' };
143
+ // Nesting contract: which subagent this row belongs to and which Task
144
+ // tool_use spawned it (parentToolUseId == the Task tool_call's callId).
145
+ msg.metadata.subagent = {
146
+ id: (subagentFileInfo && subagentFileInfo.id) || (entrySidechain && entrySidechain.id) || '',
147
+ slug: entrySlug || '',
148
+ agentType: (subagentFileInfo && subagentFileInfo.agentType) || '',
149
+ description: (subagentFileInfo && subagentFileInfo.description) || '',
150
+ parentToolUseId: (subagentFileInfo && subagentFileInfo.parentToolUseId) || '',
151
+ };
152
+ if (msg.role === 'user') {
153
+ msg.metadata.originalRole = msg.metadata.originalRole || 'user';
154
+ msg.metadata.promptKind = msg.metadata.promptKind || 'subagent';
155
+ msg.role = 'system';
156
+ msg.roleLabel = msg.roleLabel || 'Subagent prompt';
157
+ msg.agentLabel = msg.agentLabel || 'Subagent prompt';
158
+ } else if (msg.role === 'assistant') {
159
+ msg.roleLabel = msg.roleLabel || 'Subagent';
160
+ msg.agentLabel = msg.agentLabel || 'Subagent';
161
+ }
162
+ }
163
+ messages.push(msg);
164
+ return msg;
165
+ };
166
+ const lines = content.split('\n');
167
+
168
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
169
+ const line = lines[lineIdx];
170
+ if (!line.trim()) continue;
171
+ try {
172
+ const entry = JSON.parse(line);
173
+ entrySessionId = typeof entry.sessionId === 'string' ? entry.sessionId : '';
174
+ // Inline sidechain rows (Task subagents written into the SAME file by
175
+ // older Claude Code versions). Gated: relabeling user→system changes
176
+ // turn starts, so only new imports get it.
177
+ entrySlug = typeof entry.slug === 'string' ? entry.slug : '';
178
+ entrySidechain = !sourceIsSubagent && entry.isSidechain === true && structuredCaptureEnabled()
179
+ ? { id: typeof entry.agentId === 'string' ? entry.agentId : '' }
180
+ : null;
181
+ if (entry.type === 'summary' && typeof entry.summary === 'string') {
182
+ // Async-generated session title ({summary, leafUuid}); not a turn.
183
+ if (summaries && entry.summary.trim()) {
184
+ summaries.push({ summary: entry.summary.trim(), leafUuid: entry.leafUuid || '' });
185
+ }
186
+ continue;
187
+ }
188
+ if (entry.type === 'system' && entry.subtype === 'compact_boundary') {
189
+ if (structuredCaptureEnabled()) {
190
+ const compactMeta = entry.compactMetadata || {};
191
+ pushMessage(compactBoundaryMessage({
192
+ provider: 'claude',
193
+ trigger: compactMeta.trigger,
194
+ preTokens: Number(compactMeta.preTokens),
195
+ logicalParentUuid: entry.logicalParentUuid,
196
+ timestamp: entry.timestamp,
197
+ }), lineIdx);
198
+ }
199
+ continue;
200
+ }
201
+ if (entry.type === 'user' && entry.message?.role === 'user') {
202
+ const c = entry.message.content;
203
+ let text = _jsonlContentText(c);
204
+ // Detect system/tool messages masquerading as user messages
205
+ const isToolResult = Array.isArray(c) && c.some(b => b.type === 'tool_result');
206
+ // _jsonlContentText only reads text/image blocks, so a pure tool_result
207
+ // entry has empty text — structured capture still wants its blocks.
208
+ if (!text && !(isToolResult && structuredCaptureEnabled())) continue;
209
+ // Post-compact synthetic context summary: render as a collapsed
210
+ // structured row under the compact divider, never as a user prompt.
211
+ if (structuredCaptureEnabled() && (entry.isCompactSummary || entry.isVisibleInTranscriptOnly)) {
212
+ pushMessage(compactSummaryMessage({ provider: 'claude', text, timestamp: entry.timestamp }), lineIdx);
213
+ continue;
214
+ }
215
+ const isTaskNotification = text.includes('<task-notification>') || (text.includes('toolu_') && text.includes('.output'));
216
+ const isSystemReminder = text.includes('<system-reminder>') && !text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '').trim();
217
+ if (!isToolResult && !isTaskNotification && isProviderGeneratedUserContextText(text)) {
218
+ continue;
219
+ }
220
+ if (isToolResult && structuredCaptureEnabled()) {
221
+ // One structured tool_result message per block, paired to its
222
+ // tool_use at render time via callId. The top-level toolUseResult
223
+ // carries the rich result (structuredPatch for Edit, filePath,
224
+ // durationMs) — attach it when the entry has a single result block.
225
+ const resultBlocks = c.filter(b => b && b.type === 'tool_result');
226
+ const tur = resultBlocks.length === 1 && entry.toolUseResult && typeof entry.toolUseResult === 'object'
227
+ ? entry.toolUseResult : null;
228
+ for (const block of resultBlocks) {
229
+ const blockText = typeof block.content === 'string' ? block.content : _jsonlContentText(block.content);
230
+ pushMessage(toolResultMessage({
231
+ provider: 'claude',
232
+ callId: block.tool_use_id,
233
+ output: blockText || (tur && typeof tur.stdout === 'string' ? tur.stdout : ''),
234
+ isError: block.is_error === true,
235
+ filePath: tur ? (tur.filePath || tur.file_path) : undefined,
236
+ durationMs: tur ? Number(tur.durationMs) : undefined,
237
+ structuredPatch: tur ? tur.structuredPatch : undefined,
238
+ timestamp: entry.timestamp,
239
+ }), lineIdx);
240
+ }
241
+ } else if (isToolResult || isTaskNotification || isSystemReminder) {
242
+ pushMessage({ role: 'system', text, timestamp: entry.timestamp }, lineIdx);
243
+ } else {
244
+ // Strip system-reminder tags from real user messages
245
+ const cleaned = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '').trim();
246
+ pushMessage({ role: 'user', text: cleaned || text, timestamp: entry.timestamp }, lineIdx);
247
+ }
248
+ } else if (entry.type === 'user' && entry.provider === 'walle' && typeof entry.content === 'string') {
249
+ pushMessage({ role: 'user', text: entry.content, timestamp: entry.timestamp }, lineIdx);
250
+ } else if (entry.type === 'assistant' && entry.message?.role === 'assistant') {
251
+ const c = entry.message.content;
252
+ if (!Array.isArray(c)) continue;
253
+ if (structuredCaptureEnabled()) {
254
+ // Structured emission: thinking → reasoning rows, tool_use → tool_call
255
+ // rows (text stays exactly `[Tool: name]`, no longer inlined into the
256
+ // assistant text), text blocks → ONE assistant message carrying
257
+ // parentUuid + usage/model metadata.
258
+ const run = [];
259
+ const textParts = [];
260
+ let textInsertIndex = -1; // keep block order: thinking → text → tool_use
261
+ for (const block of c) {
262
+ if (!block || typeof block !== 'object') continue;
263
+ if (block.type === 'text' && block.text) {
264
+ if (textInsertIndex === -1) textInsertIndex = run.length;
265
+ textParts.push(block.text);
266
+ } else if (block.type === 'thinking' && (block.thinking || block.text)) {
267
+ run.push(reasoningMessage({ provider: 'claude', text: block.thinking || block.text, timestamp: entry.timestamp }));
268
+ } else if (block.type === 'tool_use') {
269
+ const callMsg = toolCallMessage({
270
+ provider: 'claude', tool: block.name, callId: block.id, args: block.input, timestamp: entry.timestamp,
271
+ });
272
+ // Task spawns: surface the subagent identity on the tool_call
273
+ // row so subagent rows (metadata.subagent.parentToolUseId ==
274
+ // this callId) can nest under it at render time.
275
+ if (block.name === 'Task' && block.input && typeof block.input === 'object') {
276
+ if (typeof block.input.subagent_type === 'string') callMsg.metadata.taskAgentType = block.input.subagent_type;
277
+ if (typeof block.input.description === 'string') callMsg.metadata.taskDescription = block.input.description.slice(0, 200);
278
+ }
279
+ run.push(callMsg);
280
+ }
281
+ }
282
+ if (textParts.length) {
283
+ // parentUuid rides ONLY on the assistant text message — it is the
284
+ // stream-tail merge's dedup identity; putting it on tool/reasoning
285
+ // rows would collapse them into this message.
286
+ const msg = { role: 'assistant', text: textParts.join('\n'), timestamp: entry.timestamp, parentUuid: entry.parentUuid };
287
+ const usage = _usageMetadata(entry.message.usage);
288
+ const model = typeof entry.message.model === 'string' ? entry.message.model : '';
289
+ if (usage || model) {
290
+ msg.metadata = {};
291
+ if (usage) msg.metadata.usage = usage;
292
+ if (model) msg.metadata.model = model;
293
+ }
294
+ run.splice(textInsertIndex === -1 ? run.length : textInsertIndex, 0, msg);
295
+ }
296
+ if (run.length) {
297
+ // Streaming increments re-emit the SAME provider row (same
298
+ // parentUuid) with more content. Replace the previous contiguous
299
+ // run for that parentUuid — but only when nothing else landed in
300
+ // between, so interleaved entries are never spliced away.
301
+ if (entry.parentUuid && lastAssistantRun
302
+ && lastAssistantRun.parentUuid === entry.parentUuid
303
+ && lastAssistantRun.end === messages.length) {
304
+ messages.splice(lastAssistantRun.start);
305
+ }
306
+ const start = messages.length;
307
+ for (const m of run) pushMessage(m, lineIdx);
308
+ lastAssistantRun = { parentUuid: entry.parentUuid, start, end: messages.length };
309
+ }
310
+ continue;
311
+ }
312
+ const parts = [];
313
+ for (const block of c) {
314
+ if (block.type === 'text' && block.text) {
315
+ parts.push(block.text);
316
+ } else if (block.type === 'tool_use') {
317
+ parts.push(`[Tool: ${block.name}]`);
318
+ }
319
+ }
320
+ if (parts.length > 0) {
321
+ // Deduplicate: assistant messages arrive incrementally, keep only the last one with same parentUuid.
322
+ // We surface parentUuid on the stored message (not the internal _parent) so the stream-tail merge
323
+ // (lib/conversation-tail-merge.js) and the client renderer can dedup a still-streaming partial in the
324
+ // live ring against the finalized copy in the conversation cache — the strong, timestamp-independent key.
325
+ const lastMsg = messages[messages.length - 1];
326
+ if (lastMsg && lastMsg.role === 'assistant' && lastMsg.parentUuid === entry.parentUuid) {
327
+ lastMsg.text = parts.join('\n');
328
+ if (sourceFile) {
329
+ lastMsg._sourceFile = sourceFile;
330
+ lastMsg._sourceLine = lineIdx;
331
+ }
332
+ } else {
333
+ pushMessage({ role: 'assistant', text: parts.join('\n'), timestamp: entry.timestamp, parentUuid: entry.parentUuid }, lineIdx);
334
+ }
335
+ }
336
+ } else if (entry.type === 'assistant' && entry.provider === 'walle' && typeof entry.content === 'string') {
337
+ if (entry.content) pushMessage({ role: 'assistant', text: entry.content, timestamp: entry.timestamp }, lineIdx);
338
+ } else if (entry.type === 'walle_part') {
339
+ const data = entry.data || {};
340
+ let text = '';
341
+ if (entry.partType === 'tool_call') {
342
+ text = `[Tool: ${data.name || 'tool'}]`;
343
+ } else if (entry.partType === 'tool_result') {
344
+ const result = typeof data.result === 'string' ? data.result : '';
345
+ text = `[Tool result: ${data.name || 'tool'}]${result ? '\n' + result : ''}`;
346
+ } else if (entry.partType === 'error') {
347
+ text = `[Wall-E error] ${data.message || ''}`.trim();
348
+ } else if (entry.partType === 'cancelled') {
349
+ text = '[Wall-E cancelled]';
350
+ }
351
+ if (text) pushMessage({ role: 'system', text, timestamp: entry.timestamp }, lineIdx);
352
+ }
353
+ } catch { /* skip malformed line */ }
354
+ }
355
+ }
356
+
357
+ // Read only the last `maxBytes` of a large file and parse the (line-aligned)
358
+ // tail. Moved VERBATIM from server.js so the pool tails identically.
359
+ function _parseJsonlTailIntoMessages(filePath, size, messages, opts = {}) {
360
+ const readSize = Math.min(
361
+ Math.max(1, Number(opts.maxBytes) || LARGE_SESSION_TAIL_READ_BYTES),
362
+ Math.max(0, Number(size) || 0)
363
+ );
364
+ if (!readSize) return { bytesRead: 0, partial: true };
365
+ const fd = fs.openSync(filePath, 'r');
366
+ try {
367
+ const buf = Buffer.alloc(readSize);
368
+ fs.readSync(fd, buf, 0, readSize, Math.max(0, size - readSize));
369
+ let content = buf.toString('utf8');
370
+ const nlIdx = content.indexOf('\n');
371
+ if (nlIdx > 0) content = content.slice(nlIdx + 1);
372
+ if (opts.isCodexRollout) {
373
+ const parsed = parseCodexJsonlIntoMessages(content, messages, { codexUserDeduper: opts.codexUserDeduper || opts.seenUsers });
374
+ return { ...parsed, bytesRead: readSize, partial: true };
375
+ }
376
+ _parseJsonlIntoMessages(content, messages, { sourceFile: filePath, summaries: opts.summaries });
377
+ return { bytesRead: readSize, partial: true };
378
+ } finally {
379
+ fs.closeSync(fd);
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Read + parse a session's JSONL file set into a combined, chronologically
385
+ * sorted message list. This is the body of apiSessionMessages' cache-miss path,
386
+ * extracted VERBATIM so it can run EITHER on the main thread or inside the read
387
+ * pool with byte-identical behavior (parity by construction — same code, not a
388
+ * fork). DB-free: it returns `codexParsedFiles` so the caller can write the
389
+ * Codex conversation cache on the main thread.
390
+ *
391
+ * @param {string[]} filePaths
392
+ * @param {Object} opts
393
+ * @param {boolean} [opts.latencySensitiveLargeParse] — tail-read large files under a budget
394
+ * @returns {{ messages, partialLoad, bytesRead, totalFiles, codexParsedFiles }}
395
+ */
396
+ function parseSessionFiles(filePaths, opts = {}) {
397
+ const latencySensitiveLargeParse = !!opts.latencySensitiveLargeParse;
398
+ const messages = [];
399
+ const summaries = [];
400
+ let partialLoad = false;
401
+
402
+ // Sort files by creation time (earliest first) so messages are chronological.
403
+ // `.jsonl.zst` archives (cold Codex rollouts) are materialized to the disk
404
+ // cache first so size budgets and reads apply to the decompressed bytes;
405
+ // a failed materialization (no zstd / too large) marks a partial load.
406
+ let zstUnavailable = false;
407
+ const filesWithTime = (Array.isArray(filePaths) ? filePaths : []).map(fp => {
408
+ let readPath = fp;
409
+ if (isZstRolloutFileName(fp)) {
410
+ const mat = materializeCodexRollout(fp);
411
+ if (!mat.ok) { zstUnavailable = true; return null; }
412
+ readPath = mat.path;
413
+ }
414
+ try {
415
+ const st = fs.statSync(readPath);
416
+ return { fp: readPath, originalFp: fp, birthtimeMs: st.birthtimeMs, size: st.size };
417
+ } catch { return null; }
418
+ }).filter(Boolean).sort((a, b) => a.birthtimeMs - b.birthtimeMs);
419
+ if (zstUnavailable) partialLoad = true;
420
+ const filesToParse = latencySensitiveLargeParse
421
+ ? [...filesWithTime].sort((a, b) => b.birthtimeMs - a.birthtimeMs)
422
+ : filesWithTime;
423
+
424
+ let bytesRead = 0;
425
+ let latencySensitiveBytesRead = 0;
426
+ const codexUserDeduper = createCodexUserDeduper();
427
+ const codexParsedFiles = [];
428
+ for (const { fp, size, originalFp } of filesToParse) {
429
+ // A materialized .zst lives in the cache dir — detect codex-ness from the
430
+ // ORIGINAL path.
431
+ const isCodexRollout = (originalFp || fp).includes(`${path.sep}.codex${path.sep}sessions${path.sep}`)
432
+ || (originalFp ? isZstRolloutFileName(originalFp) : false);
433
+ if (!latencySensitiveLargeParse && !isCodexRollout && bytesRead > 0 && bytesRead + size > PARSE_MAX_SYNC_SIZE && size > PARSE_SMALL_FILE_THRESHOLD) {
434
+ // Budget exceeded — skip large files, but keep going for small subagent files
435
+ partialLoad = true;
436
+ continue; // continue instead of break — small files after this will still be read
437
+ }
438
+ try {
439
+ let fileBytesRead = 0;
440
+ const shouldTailForLatency = latencySensitiveLargeParse && size > PARSE_SMALL_FILE_THRESHOLD;
441
+ if (shouldTailForLatency) {
442
+ const remainingBudget = LATENCY_SENSITIVE_SYNC_BUDGET_BYTES - latencySensitiveBytesRead;
443
+ if (remainingBudget <= 0) {
444
+ partialLoad = true;
445
+ continue;
446
+ }
447
+ const parsed = _parseJsonlTailIntoMessages(fp, size, messages, {
448
+ isCodexRollout,
449
+ codexUserDeduper,
450
+ summaries,
451
+ maxBytes: Math.min(LARGE_SESSION_TAIL_READ_BYTES, remainingBudget),
452
+ });
453
+ fileBytesRead = parsed.bytesRead || 0;
454
+ latencySensitiveBytesRead += fileBytesRead;
455
+ partialLoad = true;
456
+ } else if (isCodexRollout) {
457
+ // Codex rollouts are large because they include tool/event payloads.
458
+ // The semantic user/assistant transcript is much smaller, so stream
459
+ // the whole JSONL file and keep old eval output visible after restart.
460
+ const before = messages.length;
461
+ const parsed = parseCodexJsonlFileIntoMessages(fp, messages, { codexUserDeduper });
462
+ fileBytesRead = parsed.bytesRead || size;
463
+ codexParsedFiles.push({
464
+ fp,
465
+ size,
466
+ parsed,
467
+ messages: messages.slice(before),
468
+ });
469
+ } else if (size > LARGE_SESSION_TAIL_THRESHOLD_BYTES) {
470
+ // For very large individual files (>50MB), read only the last 10MB
471
+ // to show the most recent conversation without blocking the event loop
472
+ const parsed = _parseJsonlTailIntoMessages(fp, size, messages, { summaries });
473
+ fileBytesRead = parsed.bytesRead || 0;
474
+ partialLoad = true; // Signal that we didn't load the full file
475
+ } else {
476
+ const content = fs.readFileSync(fp, 'utf8');
477
+ fileBytesRead = size;
478
+ _parseJsonlIntoMessages(content, messages, { sourceFile: fp, summaries });
479
+ }
480
+ bytesRead += fileBytesRead;
481
+ } catch (e) { console.error(`[session-messages] error reading ${path.basename(fp).slice(0, 8)}:`, e.message); }
482
+ }
483
+
484
+ // Cross-file stitching: after /compact, Claude Code starts a NEW session
485
+ // file that INHERITS the parent file's lines (old sessionId at the top,
486
+ // switching mid-file). When the owning file is also in this set — or still
487
+ // on disk next to the child — keep only the owner's copy so merged project
488
+ // dirs don't render the inherited prefix twice. Orphan prefixes (owner file
489
+ // deleted) are kept: they are the only surviving copy.
490
+ const ownedIds = new Set();
491
+ for (const { fp } of filesWithTime) {
492
+ const owned = claudeFileSessionId(fp);
493
+ if (owned) ownedIds.add(owned);
494
+ }
495
+ const ownerOnDiskCache = new Map();
496
+ const ownerExistsOnDisk = (sourceFile, entryId) => {
497
+ const dir = path.dirname(sourceFile);
498
+ const key = `${dir}|${entryId}`;
499
+ if (!ownerOnDiskCache.has(key)) {
500
+ let exists = false;
501
+ try { exists = fs.existsSync(path.join(dir, `${entryId}.jsonl`)); } catch {}
502
+ ownerOnDiskCache.set(key, exists);
503
+ }
504
+ return ownerOnDiskCache.get(key);
505
+ };
506
+ const stitched = messages.filter(m => {
507
+ const entryId = m._entrySessionId;
508
+ if (!entryId || !m._sourceFile) return true;
509
+ const fileId = claudeFileSessionId(m._sourceFile);
510
+ if (!fileId || entryId === fileId) return true;
511
+ if (!_SESSION_FILE_UUID_RE.test(entryId)) return true;
512
+ return !(ownedIds.has(entryId) || ownerExistsOnDisk(m._sourceFile, entryId));
513
+ });
514
+
515
+ // Clean internal fields. NOTE: parentUuid is intentionally retained — it is the
516
+ // stream-tail merge's strongest dedup key (collapses a live-ring streaming partial
517
+ // against the cached finalized turn), and the client renderer reads it for data-parent-uuid.
518
+ stitched.forEach(m => {
519
+ delete m._sourceFile;
520
+ delete m._sourceLine;
521
+ delete m._entrySessionId;
522
+ });
523
+
524
+ // Sort all messages by timestamp across files
525
+ sortTimelineMessagesInPlace(stitched);
526
+
527
+ return { messages: stitched, partialLoad, bytesRead, totalFiles: filesWithTime.length, codexParsedFiles, summaries };
528
+ }
529
+
530
+ module.exports = {
531
+ _jsonlContentText,
532
+ _isClaudeSubagentJsonlPath,
533
+ _parseJsonlIntoMessages,
534
+ _parseJsonlTailIntoMessages,
535
+ parseSessionFiles,
536
+ claudeFileSessionId,
537
+ _usageMetadata,
538
+ // thresholds exported so server.js can keep its own MAX_SYNC_SIZE checks aligned
539
+ LARGE_SESSION_TAIL_THRESHOLD_BYTES,
540
+ LARGE_SESSION_TAIL_READ_BYTES,
541
+ LATENCY_SENSITIVE_SYNC_BUDGET_BYTES,
542
+ };