create-walle 0.9.22 → 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 (495) hide show
  1. package/README.md +22 -0
  2. package/package.json +1 -1
  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 +1190 -182
  11. package/template/claude-task-manager/api-reviews.js +104 -13
  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 +4071 -282
  16. package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
  17. package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
  18. package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
  19. package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
  20. package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
  21. package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
  22. package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
  23. package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
  24. package/template/claude-task-manager/docs/image-paste-ux.md +10 -0
  25. package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
  26. package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
  27. package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
  28. package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
  29. package/template/claude-task-manager/docs/phone-access-design.md +53 -15
  30. package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
  31. package/template/claude-task-manager/docs/phone-setup.md +3 -0
  32. package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
  33. package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
  34. package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
  35. package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
  36. package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
  37. package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
  38. package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
  39. package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
  40. package/template/claude-task-manager/docs/session-title-authority.md +32 -0
  41. package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
  42. package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
  43. package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
  44. package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
  45. package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
  46. package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
  47. package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
  48. package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
  49. package/template/claude-task-manager/git-utils.js +751 -10
  50. package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
  51. package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
  52. package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
  53. package/template/claude-task-manager/lib/agent-presets.js +17 -1
  54. package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
  55. package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
  56. package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
  57. package/template/claude-task-manager/lib/async-semaphore.js +44 -0
  58. package/template/claude-task-manager/lib/auth-context.js +5 -0
  59. package/template/claude-task-manager/lib/auth-rate-limit.js +24 -1
  60. package/template/claude-task-manager/lib/auth-rules.js +26 -2
  61. package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
  62. package/template/claude-task-manager/lib/background-llm.js +144 -17
  63. package/template/claude-task-manager/lib/branch-inventory.js +212 -0
  64. package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
  65. package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
  66. package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
  67. package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
  68. package/template/claude-task-manager/lib/codex-zst.js +124 -0
  69. package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
  70. package/template/claude-task-manager/lib/connection-health.js +232 -0
  71. package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
  72. package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
  73. package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
  74. package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
  75. package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
  76. package/template/claude-task-manager/lib/document-review.js +109 -5
  77. package/template/claude-task-manager/lib/escalation-review.js +152 -0
  78. package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
  79. package/template/claude-task-manager/lib/headless-term-service.js +678 -0
  80. package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
  81. package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
  82. package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
  83. package/template/claude-task-manager/lib/main-db-census.js +216 -0
  84. package/template/claude-task-manager/lib/message-pagination.js +106 -4
  85. package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +669 -28
  86. package/template/claude-task-manager/lib/mobile-auth-api.js +260 -7
  87. package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
  88. package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
  89. package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
  90. package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
  91. package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
  92. package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
  93. package/template/claude-task-manager/lib/perf-tracker.js +242 -6
  94. package/template/claude-task-manager/lib/permission-match.js +76 -0
  95. package/template/claude-task-manager/lib/permission-sync.js +133 -20
  96. package/template/claude-task-manager/lib/process-title.js +35 -0
  97. package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
  98. package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
  99. package/template/claude-task-manager/lib/prompt-intent.js +132 -0
  100. package/template/claude-task-manager/lib/provider-user-context.js +34 -0
  101. package/template/claude-task-manager/lib/read-pool-client.js +313 -0
  102. package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
  103. package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
  104. package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
  105. package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
  106. package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
  107. package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
  108. package/template/claude-task-manager/lib/restart-guard.js +46 -5
  109. package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
  110. package/template/claude-task-manager/lib/restore-policy.js +13 -0
  111. package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
  112. package/template/claude-task-manager/lib/restore-runtime.js +68 -0
  113. package/template/claude-task-manager/lib/restore-storm.js +34 -0
  114. package/template/claude-task-manager/lib/resume-cwd.js +36 -0
  115. package/template/claude-task-manager/lib/resume-preflight.js +313 -0
  116. package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
  117. package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
  118. package/template/claude-task-manager/lib/scheduler.js +21 -1
  119. package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
  120. package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
  121. package/template/claude-task-manager/lib/server-listeners.js +239 -0
  122. package/template/claude-task-manager/lib/session-capture.js +42 -7
  123. package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
  124. package/template/claude-task-manager/lib/session-history.js +388 -43
  125. package/template/claude-task-manager/lib/session-host-manager.js +287 -0
  126. package/template/claude-task-manager/lib/session-image-refs.js +209 -0
  127. package/template/claude-task-manager/lib/session-jobs.js +399 -59
  128. package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
  129. package/template/claude-task-manager/lib/session-restore.js +53 -0
  130. package/template/claude-task-manager/lib/session-standup.js +87 -10
  131. package/template/claude-task-manager/lib/session-state-bus.js +14 -0
  132. package/template/claude-task-manager/lib/session-stream.js +53 -12
  133. package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
  134. package/template/claude-task-manager/lib/session-token-usage.js +494 -0
  135. package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
  136. package/template/claude-task-manager/lib/setup-network-config.js +9 -0
  137. package/template/claude-task-manager/lib/size-cap.js +45 -0
  138. package/template/claude-task-manager/lib/size-cap.test.js +62 -0
  139. package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
  140. package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
  141. package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
  142. package/template/claude-task-manager/lib/standup-attention.js +7 -3
  143. package/template/claude-task-manager/lib/status-authority.js +39 -0
  144. package/template/claude-task-manager/lib/status-hooks.js +4 -0
  145. package/template/claude-task-manager/lib/storage-migration.js +235 -0
  146. package/template/claude-task-manager/lib/structured-capture.js +298 -0
  147. package/template/claude-task-manager/lib/sync-io-census.js +163 -0
  148. package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
  149. package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
  150. package/template/claude-task-manager/lib/terminal-choice.js +364 -0
  151. package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
  152. package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
  153. package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
  154. package/template/claude-task-manager/lib/timeline-order.js +122 -0
  155. package/template/claude-task-manager/lib/transcript-store.js +348 -43
  156. package/template/claude-task-manager/lib/transport-security.js +34 -1
  157. package/template/claude-task-manager/lib/wait-state.js +184 -0
  158. package/template/claude-task-manager/lib/walle-client.js +47 -5
  159. package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
  160. package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
  161. package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
  162. package/template/claude-task-manager/lib/walle-native-health.js +403 -0
  163. package/template/claude-task-manager/lib/walle-repair.js +701 -0
  164. package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
  165. package/template/claude-task-manager/lib/walle-session-context.js +57 -21
  166. package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
  167. package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
  168. package/template/claude-task-manager/lib/walle-transcript.js +36 -0
  169. package/template/claude-task-manager/lib/worktree-active-sync.js +5 -4
  170. package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
  171. package/template/claude-task-manager/package.json +1 -1
  172. package/template/claude-task-manager/prompt-harvest.js +89 -66
  173. package/template/claude-task-manager/providers/claude-code.js +51 -3
  174. package/template/claude-task-manager/providers/cursor.js +140 -45
  175. package/template/claude-task-manager/public/css/reviews.css +541 -61
  176. package/template/claude-task-manager/public/css/setup.css +178 -0
  177. package/template/claude-task-manager/public/css/walle-session.css +865 -10
  178. package/template/claude-task-manager/public/css/walle.css +9 -0
  179. package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
  180. package/template/claude-task-manager/public/index.html +18043 -2080
  181. package/template/claude-task-manager/public/js/document-review-links.js +106 -1
  182. package/template/claude-task-manager/public/js/image-normalize.js +69 -36
  183. package/template/claude-task-manager/public/js/message-renderer.js +1252 -75
  184. package/template/claude-task-manager/public/js/prompts.js +66 -29
  185. package/template/claude-task-manager/public/js/reviews.js +871 -127
  186. package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
  187. package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
  188. package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
  189. package/template/claude-task-manager/public/js/setup.js +1238 -181
  190. package/template/claude-task-manager/public/js/stream-view.js +671 -72
  191. package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
  192. package/template/claude-task-manager/public/js/walle-session.js +2455 -158
  193. package/template/claude-task-manager/public/js/walle.js +141 -10
  194. package/template/claude-task-manager/public/m/app.css +2033 -164
  195. package/template/claude-task-manager/public/m/app.js +5633 -433
  196. package/template/claude-task-manager/public/m/claim.html +219 -19
  197. package/template/claude-task-manager/public/m/index.html +105 -16
  198. package/template/claude-task-manager/public/m/sw.js +3 -1
  199. package/template/claude-task-manager/public/manifest.json +2 -2
  200. package/template/claude-task-manager/public/prompts.html +30 -14
  201. package/template/claude-task-manager/queue-engine.js +507 -28
  202. package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
  203. package/template/claude-task-manager/server.js +13981 -2107
  204. package/template/claude-task-manager/session-integrity.js +156 -18
  205. package/template/claude-task-manager/session-search-ranking.js +1 -0
  206. package/template/claude-task-manager/session-utils.js +25 -5
  207. package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
  208. package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
  209. package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
  210. package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
  211. package/template/claude-task-manager/workers/harvest-worker.js +9 -55
  212. package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
  213. package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
  214. package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
  215. package/template/claude-task-manager/workers/session-host-process.js +146 -0
  216. package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
  217. package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
  218. package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
  219. package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
  220. package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
  221. package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
  222. package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
  223. package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
  224. package/template/docs/design/markdown-review-pane.md +206 -0
  225. package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +54 -14
  226. package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
  227. package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
  228. package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
  229. package/template/docs/private-memory-and-pii-policy.md +69 -0
  230. package/template/package.json +2 -1
  231. package/template/scripts/check-private-data.js +201 -0
  232. package/template/shared/sqlite-owner-guard.js +30 -0
  233. package/template/shared/sqlite-owner-write-queue.js +225 -0
  234. package/template/shared/sqlite-storage-policy.js +111 -0
  235. package/template/shared/sqlite-write-lock.js +428 -0
  236. package/template/wall-e/agent-runners/claude-code.js +5 -0
  237. package/template/wall-e/agent.js +166 -22
  238. package/template/wall-e/api-walle.js +505 -69
  239. package/template/wall-e/auth/provider-flows.js +11 -1
  240. package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
  241. package/template/wall-e/brain.js +1463 -136
  242. package/template/wall-e/chat/attachment-blocks.js +96 -0
  243. package/template/wall-e/chat/attachments.js +2 -1
  244. package/template/wall-e/chat/capability-resolver.js +7 -7
  245. package/template/wall-e/chat/context-messages.js +28 -0
  246. package/template/wall-e/chat/conversation-frame.js +630 -0
  247. package/template/wall-e/chat/provider-messages.js +125 -0
  248. package/template/wall-e/chat.js +926 -242
  249. package/template/wall-e/coding/acceptance-contract.js +170 -0
  250. package/template/wall-e/coding/acp-adapter.js +1 -1
  251. package/template/wall-e/coding/agent-catalog.js +3 -0
  252. package/template/wall-e/coding/artifact-store.js +93 -0
  253. package/template/wall-e/coding/capability-router.js +120 -0
  254. package/template/wall-e/coding/coding-run-controller.js +423 -0
  255. package/template/wall-e/coding/compaction-service.js +157 -12
  256. package/template/wall-e/coding/frontend-verification.js +258 -0
  257. package/template/wall-e/coding/lifecycle-hooks.js +75 -0
  258. package/template/wall-e/coding/local-preview-contract.js +157 -0
  259. package/template/wall-e/coding/permission-service.js +57 -13
  260. package/template/wall-e/coding/prompt-bundle.js +19 -1
  261. package/template/wall-e/coding/prompt-section-registry.js +227 -0
  262. package/template/wall-e/coding/provider-compat.js +15 -0
  263. package/template/wall-e/coding/runtime-events.js +224 -0
  264. package/template/wall-e/coding/runtime-mode.js +3 -0
  265. package/template/wall-e/coding/side-git-snapshot.js +160 -4
  266. package/template/wall-e/coding/snapshot-service.js +143 -1
  267. package/template/wall-e/coding/stream-processor.js +388 -34
  268. package/template/wall-e/coding/task-tool.js +141 -4
  269. package/template/wall-e/coding/tool-execution-controller.js +365 -0
  270. package/template/wall-e/coding/tool-registry.js +43 -5
  271. package/template/wall-e/coding/user-hooks.js +217 -0
  272. package/template/wall-e/coding-orchestrator.js +1224 -209
  273. package/template/wall-e/coding-prompts.js +20 -4
  274. package/template/wall-e/context/context-builder.js +15 -2
  275. package/template/wall-e/decision/confidence.js +1 -1
  276. package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
  277. package/template/wall-e/docs/external-action-controller.md +26 -6
  278. package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
  279. package/template/wall-e/embeddings.js +591 -53
  280. package/template/wall-e/external-action-controller.js +12 -0
  281. package/template/wall-e/http/auth.js +1 -0
  282. package/template/wall-e/http/chat-api.js +46 -11
  283. package/template/wall-e/http/model-admin.js +727 -56
  284. package/template/wall-e/lib/boot-profile.js +88 -0
  285. package/template/wall-e/lib/event-loop-monitor.js +93 -0
  286. package/template/wall-e/llm/anthropic.js +123 -5
  287. package/template/wall-e/llm/client.js +236 -67
  288. package/template/wall-e/llm/default-fallback.js +382 -0
  289. package/template/wall-e/llm/health.js +19 -0
  290. package/template/wall-e/llm/message-guard.js +78 -0
  291. package/template/wall-e/llm/model-catalog.js +252 -1
  292. package/template/wall-e/llm/openai.js +10 -3
  293. package/template/wall-e/llm/portkey-sync.js +489 -36
  294. package/template/wall-e/llm/provider-error.js +30 -2
  295. package/template/wall-e/llm/registry.js +5 -1
  296. package/template/wall-e/llm/request-compat.js +67 -0
  297. package/template/wall-e/loops/backfill.js +79 -23
  298. package/template/wall-e/loops/brain-optimize.js +67 -0
  299. package/template/wall-e/loops/ingest.js +25 -10
  300. package/template/wall-e/loops/question-digest.js +160 -0
  301. package/template/wall-e/loops/reflect.js +6 -4
  302. package/template/wall-e/loops/think.js +39 -12
  303. package/template/wall-e/mcp-server.js +318 -36
  304. package/template/wall-e/memory/ctm-context-client.js +52 -14
  305. package/template/wall-e/memory/ctm-operational-context.js +237 -0
  306. package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
  307. package/template/wall-e/memory/ctm-session-context.js +111 -63
  308. package/template/wall-e/prompts/coding/deepseek.txt +3 -0
  309. package/template/wall-e/prompts/coding/gemini.txt +6 -0
  310. package/template/wall-e/prompts/coding/gpt.txt +6 -0
  311. package/template/wall-e/prompts/coding/local.txt +7 -0
  312. package/template/wall-e/runtime/decision-hooks.js +115 -0
  313. package/template/wall-e/runtime/devbox-gateway.js +82 -8
  314. package/template/wall-e/runtime/prompt-manifest.js +86 -0
  315. package/template/wall-e/runtime/tool-executor.js +269 -0
  316. package/template/wall-e/runtime/tool-result-envelope.js +138 -0
  317. package/template/wall-e/runtime/transcript-projection.js +60 -0
  318. package/template/wall-e/runtime/walle-runtime.js +224 -0
  319. package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
  320. package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
  321. package/template/wall-e/server.js +2 -0
  322. package/template/wall-e/session-files.js +9 -0
  323. package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
  324. package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
  325. package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
  326. package/template/wall-e/skills/claude-code-reader.js +7 -3
  327. package/template/wall-e/skills/script-skill-runner.js +10 -0
  328. package/template/wall-e/skills/skill-planner.js +38 -0
  329. package/template/wall-e/tools/builtin-middleware.js +19 -9
  330. package/template/wall-e/tools/local-tools.js +1428 -16
  331. package/template/wall-e/tools/permission-checker.js +73 -5
  332. package/template/wall-e/tools/question-manager.js +117 -7
  333. package/template/wall-e/training/harvester.js +12 -28
  334. package/template/wall-e/training/replay.js +25 -80
  335. package/template/wall-e/eval/ab-test.js +0 -203
  336. package/template/wall-e/eval/agent-runner.js +0 -772
  337. package/template/wall-e/eval/agent-scorer.js +0 -461
  338. package/template/wall-e/eval/aggregator.js +0 -414
  339. package/template/wall-e/eval/allowed-test-commands.js +0 -34
  340. package/template/wall-e/eval/benchmark-generator.js +0 -113
  341. package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
  342. package/template/wall-e/eval/benchmarks/chat.json +0 -82
  343. package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
  344. package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
  345. package/template/wall-e/eval/benchmarks/coding.json +0 -122
  346. package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
  347. package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
  348. package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
  349. package/template/wall-e/eval/benchmarks.js +0 -669
  350. package/template/wall-e/eval/cc-replay.js +0 -719
  351. package/template/wall-e/eval/chat-eval.js +0 -525
  352. package/template/wall-e/eval/check-keys.js +0 -15
  353. package/template/wall-e/eval/check-providers.js +0 -42
  354. package/template/wall-e/eval/codex-cli-baseline.js +0 -669
  355. package/template/wall-e/eval/coding-agent-real.js +0 -570
  356. package/template/wall-e/eval/context-compactor.js +0 -251
  357. package/template/wall-e/eval/debug-agent003.js +0 -68
  358. package/template/wall-e/eval/diagnostics.js +0 -216
  359. package/template/wall-e/eval/eval-orchestrator.js +0 -642
  360. package/template/wall-e/eval/evaluate.js +0 -202
  361. package/template/wall-e/eval/evaluator.js +0 -373
  362. package/template/wall-e/eval/exporter.js +0 -212
  363. package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
  364. package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
  365. package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
  366. package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
  367. package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
  368. package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
  369. package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
  370. package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
  371. package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
  372. package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
  373. package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
  374. package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
  375. package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
  376. package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
  377. package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
  378. package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
  379. package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
  380. package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
  381. package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
  382. package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
  383. package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
  384. package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
  385. package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
  386. package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
  387. package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
  388. package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
  389. package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
  390. package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
  391. package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
  392. package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
  393. package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
  394. package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
  395. package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
  396. package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
  397. package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
  398. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
  399. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
  400. package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
  401. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
  402. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
  403. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
  404. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
  405. package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
  406. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
  407. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
  408. package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
  409. package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
  410. package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
  411. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
  412. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
  413. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
  414. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
  415. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
  416. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
  417. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
  418. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
  419. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
  420. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
  421. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
  422. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
  423. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
  424. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
  425. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
  426. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
  427. package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
  428. package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
  429. package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
  430. package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
  431. package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
  432. package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
  433. package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
  434. package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
  435. package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
  436. package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
  437. package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
  438. package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
  439. package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
  440. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
  441. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
  442. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
  443. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
  444. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
  445. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
  446. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
  447. package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
  448. package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
  449. package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
  450. package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
  451. package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
  452. package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
  453. package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
  454. package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
  455. package/template/wall-e/eval/harvester.js +0 -685
  456. package/template/wall-e/eval/head-to-head.js +0 -388
  457. package/template/wall-e/eval/humaneval-adapter.js +0 -321
  458. package/template/wall-e/eval/list-models.js +0 -31
  459. package/template/wall-e/eval/livecodebench-adapter.js +0 -291
  460. package/template/wall-e/eval/mail-integration.js +0 -443
  461. package/template/wall-e/eval/manifest.js +0 -186
  462. package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
  463. package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
  464. package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
  465. package/template/wall-e/eval/meta-harness/cli.js +0 -86
  466. package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
  467. package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
  468. package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
  469. package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
  470. package/template/wall-e/eval/meta-harness/frontier.js +0 -96
  471. package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
  472. package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
  473. package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
  474. package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
  475. package/template/wall-e/eval/meta-harness/reporting.js +0 -58
  476. package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
  477. package/template/wall-e/eval/meta-harness/validation.js +0 -81
  478. package/template/wall-e/eval/promoter.js +0 -228
  479. package/template/wall-e/eval/provider-normalizer.js +0 -33
  480. package/template/wall-e/eval/replay.js +0 -395
  481. package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
  482. package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
  483. package/template/wall-e/eval/run-coding-agent-real.js +0 -187
  484. package/template/wall-e/eval/run-eval.js +0 -435
  485. package/template/wall-e/eval/run-model-comparison.js +0 -142
  486. package/template/wall-e/eval/session-evaluator.js +0 -187
  487. package/template/wall-e/eval/session-miner.js +0 -207
  488. package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
  489. package/template/wall-e/eval/session-transcripts.js +0 -509
  490. package/template/wall-e/eval/shadow.js +0 -161
  491. package/template/wall-e/eval/swebench-adapter.js +0 -345
  492. package/template/wall-e/eval/swebench-docker.js +0 -192
  493. package/template/wall-e/eval/train.py +0 -320
  494. package/template/wall-e/eval/trainer.js +0 -232
  495. package/template/wall-e/eval/weekly-eval-loop.js +0 -241
@@ -4,11 +4,28 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const { StringDecoder } = require('string_decoder');
6
6
  const { getResumeSpec: getAgentResumeSpec, normalizeAgentType } = require('./agent-capabilities');
7
+ const structuredCapture = require('./structured-capture');
8
+ const codexZst = require('./codex-zst');
7
9
 
8
10
  const DEFAULT_CODEX_LOOKBACK_SEC = 10;
9
11
  const DEFAULT_CODEX_MAX_DELAY_SEC = 180;
10
12
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
11
- const CODEX_ROLLOUT_BASENAME_RE = /^rollout-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.bak)?$/i;
13
+ const CODEX_ROLLOUT_BASENAME_RE = /^rollout-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.bak)?(?:\.zst)?$/i;
14
+
15
+ // Rollout candidates on disk: live `.jsonl` plus zstd-compressed cold
16
+ // archives (`.jsonl.zst`) that newer Codex versions write.
17
+ function isCodexRolloutCandidateName(name) {
18
+ const n = String(name || '');
19
+ return n.endsWith('.jsonl') || codexZst.isZstRolloutFileName(n);
20
+ }
21
+
22
+ // Compressed archives are materialized to a disk cache before any read; a
23
+ // failure (no zstd, too large) makes the file invisible to the reader.
24
+ function _codexReadablePath(filePath) {
25
+ if (!codexZst.isZstRolloutFileName(filePath)) return filePath;
26
+ const mat = codexZst.materializeCodexRollout(filePath);
27
+ return mat.ok ? mat.path : null;
28
+ }
12
29
 
13
30
  function getResumeSpec(agentType, resumeId) {
14
31
  return getAgentResumeSpec(agentType || 'claude', resumeId);
@@ -151,7 +168,7 @@ function findCodexSessionFiles(threadId, sessionsRoot, homeDir = process.env.HOM
151
168
  if (entry.isFile() && codexRolloutIdFromPath(fullPath) === wanted) {
152
169
  if (!codexRolloutFileMatchesThread(fullPath, wanted)) continue;
153
170
  matches.push(fullPath);
154
- } else if (entry.isFile() && entry.name.endsWith('.jsonl')) {
171
+ } else if (entry.isFile() && isCodexRolloutCandidateName(entry.name)) {
155
172
  metadataFallbackCandidates.push(fullPath);
156
173
  }
157
174
  }
@@ -161,7 +178,7 @@ function findCodexSessionFiles(threadId, sessionsRoot, homeDir = process.env.HOM
161
178
  if (codexRolloutFileMatchesThread(fullPath, wanted)) matches.push(fullPath);
162
179
  }
163
180
  }
164
- const sorted = sortCodexRolloutFiles(matches);
181
+ const sorted = sortCodexRolloutFiles(codexZst.dropZstWithLiveSibling(matches));
165
182
  return indexedPathMatchesWanted
166
183
  ? [indexedPath, ...sorted.filter((filePath) => filePath !== indexedPath)]
167
184
  : sorted;
@@ -188,7 +205,7 @@ function findCodexSessionFilesByFilename(threadId, sessionsRoot, homeDir = proce
188
205
  }
189
206
  }
190
207
  }
191
- return sortCodexRolloutFiles(matches);
208
+ return sortCodexRolloutFiles(codexZst.dropZstWithLiveSibling(matches));
192
209
  }
193
210
 
194
211
  function parseSessionStartMs(value) {
@@ -454,10 +471,10 @@ function repairCodexStateThreadRollout(threadId, expectedRolloutPath, opts = {})
454
471
  const wanted = String(threadId || '').trim().toLowerCase();
455
472
  const rolloutPath = String(expectedRolloutPath || '').trim();
456
473
  const homeDir = opts.homeDir || process.env.HOME;
457
- if (!UUID_RE.test(wanted)) return { ok: false, repaired: false, mismatch: false, reason: 'invalid_thread_id' };
458
- if (!rolloutPath || !fs.existsSync(rolloutPath)) return { ok: false, repaired: false, mismatch: false, reason: 'expected_rollout_missing' };
459
- if (codexRolloutIdFromPath(rolloutPath) !== wanted) return { ok: false, repaired: false, mismatch: false, reason: 'expected_rollout_filename_mismatch' };
460
- if (!codexRolloutFileMatchesThread(rolloutPath, wanted)) return { ok: false, repaired: false, mismatch: false, reason: 'expected_rollout_metadata_mismatch' };
474
+ if (!UUID_RE.test(wanted)) return { ok: false, repaired: false, mismatch: false, blocked: false, reason: 'invalid_thread_id' };
475
+ if (!rolloutPath || !fs.existsSync(rolloutPath)) return { ok: false, repaired: false, mismatch: false, blocked: false, reason: 'expected_rollout_missing' };
476
+ if (codexRolloutIdFromPath(rolloutPath) !== wanted) return { ok: false, repaired: false, mismatch: false, blocked: false, reason: 'expected_rollout_filename_mismatch' };
477
+ if (!codexRolloutFileMatchesThread(rolloutPath, wanted)) return { ok: false, repaired: false, mismatch: false, blocked: false, reason: 'expected_rollout_metadata_mismatch' };
461
478
 
462
479
  const before = inspectCodexStateThreadRollout(wanted, rolloutPath, { homeDir });
463
480
  if (before.ok) return { ...before, repaired: false, blocked: false };
@@ -656,9 +673,31 @@ function codexThreadPreciseCreatedMs(row, homeDir = process.env.HOME) {
656
673
  return fallback;
657
674
  }
658
675
 
676
+ function codexSessionLineageFromPayload(payload = {}) {
677
+ const sourcePayload = payload.source && typeof payload.source === 'object' ? payload.source : null;
678
+ const subagent = sourcePayload?.subagent || {};
679
+ const threadSpawn = subagent.thread_spawn || {};
680
+ const parentThreadId = payload.parent_thread_id
681
+ || payload.parent_agent_session_id
682
+ || threadSpawn.parent_thread_id
683
+ || '';
684
+ const threadSource = payload.thread_source
685
+ || (parentThreadId ? 'subagent' : '');
686
+ return {
687
+ source: typeof payload.source === 'string' ? payload.source : (threadSource || ''),
688
+ thread_source: threadSource,
689
+ parent_agent_session_id: parentThreadId,
690
+ parent_thread_id: parentThreadId,
691
+ agent_nickname: payload.agent_nickname || subagent.agent_nickname || threadSpawn.agent_nickname || '',
692
+ agent_role: payload.agent_role || subagent.agent_role || threadSpawn.agent_role || '',
693
+ };
694
+ }
695
+
659
696
  function _readCodexRolloutMetadata(filePath) {
697
+ const readPath = _codexReadablePath(filePath);
698
+ if (!readPath) return null;
660
699
  // 512KB covers the large turn_context blob that Codex injects before the user_message event
661
- const prefix = readFilePrefix(filePath, 512 * 1024);
700
+ const prefix = readFilePrefix(readPath, 512 * 1024);
662
701
  if (!prefix) return null;
663
702
  let meta = null;
664
703
  let firstUser = '';
@@ -669,12 +708,17 @@ function _readCodexRolloutMetadata(filePath) {
669
708
  if (!meta && entry?.type === 'session_meta') {
670
709
  const payload = entry.payload || {};
671
710
  if (!payload.id) return null;
711
+ const lineage = codexSessionLineageFromPayload(payload);
672
712
  meta = {
673
713
  id: payload.id,
674
714
  cwd: payload.cwd || '',
675
715
  model: payload.model || payload.model_id || '',
676
716
  model_provider: payload.model_provider || '',
677
- source: payload.source || '',
717
+ source: lineage.source,
718
+ thread_source: lineage.thread_source,
719
+ parent_agent_session_id: lineage.parent_agent_session_id,
720
+ agent_nickname: lineage.agent_nickname,
721
+ agent_role: lineage.agent_role,
678
722
  cli_version: payload.cli_version || '',
679
723
  git_branch: payload.git_branch || '',
680
724
  createdMs: parseSessionStartMs(payload.timestamp || entry.timestamp),
@@ -694,6 +738,11 @@ function _readCodexRolloutMetadata(filePath) {
694
738
  model: meta.model,
695
739
  model_provider: meta.model_provider,
696
740
  source: meta.source,
741
+ thread_source: meta.thread_source,
742
+ parent_agent_session_id: meta.parent_agent_session_id,
743
+ parent_thread_id: meta.parent_agent_session_id,
744
+ agent_nickname: meta.agent_nickname,
745
+ agent_role: meta.agent_role,
697
746
  cli_version: meta.cli_version,
698
747
  cwd: meta.cwd,
699
748
  git_branch: meta.git_branch,
@@ -792,7 +841,7 @@ function findCodexThreadFromRolloutsForSession({
792
841
  stack.push(fullPath);
793
842
  continue;
794
843
  }
795
- if (!entry.isFile() || !entry.name.endsWith('.jsonl')) continue;
844
+ if (!entry.isFile() || !isCodexRolloutCandidateName(entry.name)) continue;
796
845
  const row = _readCodexRolloutMetadata(fullPath);
797
846
  if (!row || !codexCwdMatches(row.cwd, cwd)) continue;
798
847
  const ms = row._preciseCreatedMs;
@@ -922,6 +971,71 @@ function codexUserKey(text) {
922
971
  return String(text || '').replace(/\s+/g, ' ').trim().slice(0, 500);
923
972
  }
924
973
 
974
+ const CODEX_USER_ECHO_DEDUP_WINDOW_MS = 5000;
975
+
976
+ function _codexTimestampMs(timestamp) {
977
+ const value = String(timestamp || '').trim();
978
+ if (!value) return null;
979
+ const ms = Date.parse(value);
980
+ return Number.isFinite(ms) ? ms : null;
981
+ }
982
+
983
+ function createCodexUserDeduper(seedMessages = [], options = {}) {
984
+ const windowMs = Math.max(0, Number(options.windowMs ?? CODEX_USER_ECHO_DEDUP_WINDOW_MS));
985
+ const byText = new Map();
986
+
987
+ const rememberKey = (key, timestamp) => {
988
+ if (!key) return false;
989
+ const tsMs = _codexTimestampMs(timestamp);
990
+ let seen = byText.get(key);
991
+ if (!seen) {
992
+ seen = [];
993
+ byText.set(key, seen);
994
+ }
995
+ for (const prior of seen) {
996
+ if (tsMs == null || prior == null) return false;
997
+ if (Math.abs(tsMs - prior) <= windowMs) return false;
998
+ }
999
+ seen.push(tsMs);
1000
+ if (seen.length > 256) seen.splice(0, seen.length - 256);
1001
+ return true;
1002
+ };
1003
+
1004
+ const deduper = {
1005
+ remember(text, timestamp) {
1006
+ return rememberKey(codexUserKey(text), timestamp);
1007
+ },
1008
+ has(text, timestamp) {
1009
+ const key = codexUserKey(text);
1010
+ if (!key) return false;
1011
+ const tsMs = _codexTimestampMs(timestamp);
1012
+ const seen = byText.get(key) || [];
1013
+ return seen.some((prior) => {
1014
+ if (tsMs == null || prior == null) return true;
1015
+ return Math.abs(tsMs - prior) <= windowMs;
1016
+ });
1017
+ },
1018
+ seed(message) {
1019
+ if (!message) return false;
1020
+ const text = message.text || message.content || message.message || '';
1021
+ const timestamp = message.timestamp || message.created_at || message.createdAt || '';
1022
+ return this.remember(text, timestamp);
1023
+ },
1024
+ };
1025
+
1026
+ if (seedMessages instanceof Set) {
1027
+ for (const key of seedMessages) rememberKey(key, null);
1028
+ } else {
1029
+ for (const message of Array.isArray(seedMessages) ? seedMessages : []) deduper.seed(message);
1030
+ }
1031
+ return deduper;
1032
+ }
1033
+
1034
+ function normalizeCodexUserDeduper(value) {
1035
+ if (value && typeof value.remember === 'function') return value;
1036
+ return createCodexUserDeduper(value instanceof Set ? value : []);
1037
+ }
1038
+
925
1039
  function codexInputText(content) {
926
1040
  if (typeof content === 'string') return content;
927
1041
  if (!Array.isArray(content)) return '';
@@ -940,6 +1054,113 @@ function codexAssistantText(content) {
940
1054
  .trim();
941
1055
  }
942
1056
 
1057
+ // Structured capture for Codex rollout items the legacy text parser drops:
1058
+ // reasoning, function/tool/shell calls + outputs, apply_patch, web search,
1059
+ // and compaction markers. Returns ONE message, an ARRAY of messages, or null.
1060
+ // Kept separate from codexMessageFromEntry on purpose — session-stream,
1061
+ // transcript-store, and relink-audit consume that function directly and must
1062
+ // keep their user/assistant-only behavior. Only appendCodexEntryMessage (the
1063
+ // conversation parse path) consults this.
1064
+ function codexStructuredMessageFromEntry(entry) {
1065
+ if (!entry || typeof entry !== 'object') return null;
1066
+ const timestamp = entry.timestamp || '';
1067
+
1068
+ if (entry.type === 'compacted') {
1069
+ const out = [structuredCapture.compactBoundaryMessage({ provider: 'codex', timestamp })];
1070
+ const summaryText = typeof entry.payload?.message === 'string' ? entry.payload.message.trim() : '';
1071
+ if (summaryText) {
1072
+ out.push(structuredCapture.compactSummaryMessage({ provider: 'codex', text: summaryText, timestamp }));
1073
+ }
1074
+ return out;
1075
+ }
1076
+
1077
+ if (entry.type !== 'response_item' || !entry.payload || typeof entry.payload !== 'object') return null;
1078
+ const p = entry.payload;
1079
+
1080
+ switch (p.type) {
1081
+ case 'reasoning': {
1082
+ const blockText = (blocks) => Array.isArray(blocks)
1083
+ ? blocks.filter((b) => b && typeof b.text === 'string' && b.text).map((b) => b.text).join('\n')
1084
+ : '';
1085
+ const text = (blockText(p.summary) || blockText(p.content) || '').trim();
1086
+ if (text) return structuredCapture.reasoningMessage({ provider: 'codex', text, timestamp });
1087
+ if (p.encrypted_content) return structuredCapture.reasoningMessage({ provider: 'codex', encrypted: true, timestamp });
1088
+ return null;
1089
+ }
1090
+ case 'function_call': {
1091
+ const name = String(p.name || '').trim();
1092
+ let parsedArgs = null;
1093
+ if (typeof p.arguments === 'string') { try { parsedArgs = JSON.parse(p.arguments); } catch {} }
1094
+ else if (p.arguments && typeof p.arguments === 'object') parsedArgs = p.arguments;
1095
+ const patch = structuredCapture.detectApplyPatch({
1096
+ name,
1097
+ argumentsJson: p.arguments,
1098
+ command: parsedArgs && parsedArgs.command,
1099
+ });
1100
+ if (patch) {
1101
+ return structuredCapture.patchMessage({
1102
+ provider: 'codex', patchText: patch.patchText, files: patch.files, callId: p.call_id, timestamp,
1103
+ });
1104
+ }
1105
+ if ((name === 'shell' || name === 'container.exec' || name === 'exec_command')
1106
+ && parsedArgs && parsedArgs.command) {
1107
+ return structuredCapture.shellMessage({ provider: 'codex', command: parsedArgs.command, callId: p.call_id, timestamp });
1108
+ }
1109
+ return structuredCapture.toolCallMessage({ provider: 'codex', tool: name || 'tool', callId: p.call_id, args: p.arguments, timestamp });
1110
+ }
1111
+ case 'function_call_output':
1112
+ case 'custom_tool_call_output': {
1113
+ let outputText = typeof p.output === 'string' ? p.output
1114
+ : (p.output != null ? JSON.stringify(p.output) : '');
1115
+ let exitCode;
1116
+ let durationMs;
1117
+ if (outputText.startsWith('{')) {
1118
+ // Shell outputs arrive as a JSON envelope: {"output", "metadata": {"exit_code", "duration_seconds"}}
1119
+ try {
1120
+ const parsed = JSON.parse(outputText);
1121
+ if (parsed && typeof parsed === 'object') {
1122
+ if (typeof parsed.output === 'string') outputText = parsed.output;
1123
+ const md = parsed.metadata || {};
1124
+ if (Number.isFinite(Number(md.exit_code))) exitCode = Number(md.exit_code);
1125
+ if (Number.isFinite(Number(md.duration_seconds))) durationMs = Math.round(Number(md.duration_seconds) * 1000);
1126
+ }
1127
+ } catch {}
1128
+ }
1129
+ return structuredCapture.toolResultMessage({
1130
+ provider: 'codex', callId: p.call_id, output: outputText, exitCode, durationMs, timestamp,
1131
+ });
1132
+ }
1133
+ case 'local_shell_call': {
1134
+ const action = p.action || {};
1135
+ const command = Array.isArray(action.command) ? action.command : [];
1136
+ const patch = structuredCapture.detectApplyPatch({ command });
1137
+ if (patch) {
1138
+ return structuredCapture.patchMessage({
1139
+ provider: 'codex', patchText: patch.patchText, files: patch.files, callId: p.call_id, timestamp,
1140
+ });
1141
+ }
1142
+ return structuredCapture.shellMessage({ provider: 'codex', command, callId: p.call_id, timestamp });
1143
+ }
1144
+ case 'custom_tool_call': {
1145
+ const name = String(p.name || '').trim();
1146
+ const patch = structuredCapture.detectApplyPatch({ name, input: p.input });
1147
+ if (patch) {
1148
+ return structuredCapture.patchMessage({
1149
+ provider: 'codex', patchText: patch.patchText, files: patch.files, callId: p.call_id, timestamp,
1150
+ });
1151
+ }
1152
+ return structuredCapture.toolCallMessage({ provider: 'codex', tool: name || 'tool', callId: p.call_id, args: p.input, timestamp });
1153
+ }
1154
+ case 'web_search_call': {
1155
+ const action = p.action || {};
1156
+ const query = String(action.query || p.query || '').trim();
1157
+ return structuredCapture.webSearchMessage({ provider: 'codex', query: query || 'web search', timestamp });
1158
+ }
1159
+ default:
1160
+ return null;
1161
+ }
1162
+ }
1163
+
943
1164
  function codexMessageFromEntry(entry) {
944
1165
  if (!entry || typeof entry !== 'object') return null;
945
1166
 
@@ -964,64 +1185,93 @@ function codexMessageFromEntry(entry) {
964
1185
  function codexSessionMetaFromEntry(entry) {
965
1186
  if (!entry || entry.type !== 'session_meta') return null;
966
1187
  const payload = entry.payload || {};
1188
+ const lineage = codexSessionLineageFromPayload(payload);
967
1189
  return {
968
1190
  id: payload.id || '',
969
1191
  cwd: payload.cwd || '',
970
1192
  model: payload.model || payload.model_id || '',
971
1193
  git_branch: payload.git_branch || '',
972
1194
  timestamp: payload.timestamp || entry.timestamp || '',
1195
+ source: lineage.source,
1196
+ thread_source: lineage.thread_source,
1197
+ parent_agent_session_id: lineage.parent_agent_session_id,
1198
+ parent_thread_id: lineage.parent_thread_id,
1199
+ agent_nickname: lineage.agent_nickname,
1200
+ agent_role: lineage.agent_role,
973
1201
  };
974
1202
  }
975
1203
 
976
1204
  function appendCodexEntryMessage(entry, messages, seenUsers) {
977
1205
  if (!entry || typeof entry !== 'object') return false;
978
1206
  const msg = codexMessageFromEntry(entry);
979
- if (!msg) return false;
1207
+ if (!msg) {
1208
+ // Structured capture: items the text parser drops (reasoning, tool/shell
1209
+ // calls + outputs, patches, web search, compaction). role:'system' rows —
1210
+ // the user echo deduper does not apply.
1211
+ if (!structuredCapture.structuredCaptureEnabled()) return false;
1212
+ const structured = codexStructuredMessageFromEntry(entry);
1213
+ if (!structured) return false;
1214
+ for (const m of Array.isArray(structured) ? structured : [structured]) messages.push(m);
1215
+ return true;
1216
+ }
980
1217
  if (msg.role === 'user') {
981
- const key = codexUserKey(msg.text);
982
- if (!key || seenUsers.has(key)) return false;
983
- seenUsers.add(key);
1218
+ const deduper = normalizeCodexUserDeduper(seenUsers);
1219
+ if (!deduper.remember(msg.text, msg.timestamp)) return false;
984
1220
  }
985
1221
  messages.push(msg);
986
1222
  return true;
987
1223
  }
988
1224
 
989
- function parseCodexJsonlLine(line, messages, state) {
990
- if (!line || !line.trim()) return false;
1225
+ function parseCodexJsonlRecord(line, messages, state) {
1226
+ if (!line || !line.trim()) return { valid: true, messageAdded: false };
991
1227
  let entry;
992
- try { entry = JSON.parse(line); } catch { return false; }
993
- if (!entry || typeof entry !== 'object') return false;
1228
+ try { entry = JSON.parse(line); } catch { return { valid: false, messageAdded: false }; }
1229
+ if (!entry || typeof entry !== 'object') return { valid: false, messageAdded: false };
994
1230
  if (!state.sessionMeta) {
995
1231
  const meta = codexSessionMetaFromEntry(entry);
996
1232
  if (meta?.id) state.sessionMeta = meta;
997
1233
  }
998
- return appendCodexEntryMessage(entry, messages, state.seenUsers);
1234
+ return { valid: true, messageAdded: appendCodexEntryMessage(entry, messages, state.seenUsers) };
1235
+ }
1236
+
1237
+ function parseCodexJsonlLine(line, messages, state) {
1238
+ return parseCodexJsonlRecord(line, messages, state).messageAdded;
999
1239
  }
1000
1240
 
1001
1241
  function parseCodexJsonlIntoMessages(content, messages, options = {}) {
1002
1242
  const raw = String(content || '');
1003
1243
  const state = {
1004
- seenUsers: options.seenUsers || new Set(),
1244
+ seenUsers: normalizeCodexUserDeduper(options.codexUserDeduper || options.seenUsers),
1005
1245
  sessionMeta: null,
1006
1246
  };
1007
1247
  const before = messages.length;
1008
1248
  let linesRead = 0;
1249
+ let completeBytesRead = 0;
1009
1250
 
1010
1251
  let start = 0;
1011
- while (start <= raw.length) {
1012
- let end = raw.indexOf('\n', start);
1013
- if (end === -1) end = raw.length;
1252
+ while (start < raw.length) {
1253
+ const end = raw.indexOf('\n', start);
1254
+ if (end === -1) {
1255
+ const line = raw.slice(start);
1256
+ if (line) {
1257
+ linesRead++;
1258
+ const parsed = parseCodexJsonlRecord(line, messages, state);
1259
+ if (parsed.valid) completeBytesRead += Buffer.byteLength(line);
1260
+ }
1261
+ break;
1262
+ }
1014
1263
  const line = raw.slice(start, end);
1264
+ completeBytesRead += Buffer.byteLength(line) + 1;
1015
1265
  if (line) {
1016
1266
  linesRead++;
1017
- parseCodexJsonlLine(line, messages, state);
1267
+ parseCodexJsonlRecord(line, messages, state);
1018
1268
  }
1019
- if (end === raw.length) break;
1020
1269
  start = end + 1;
1021
1270
  }
1022
1271
 
1023
1272
  return {
1024
1273
  bytesRead: Buffer.byteLength(raw),
1274
+ completeBytesRead,
1025
1275
  linesRead,
1026
1276
  messagesAdded: messages.length - before,
1027
1277
  sessionMeta: state.sessionMeta,
@@ -1029,8 +1279,12 @@ function parseCodexJsonlIntoMessages(content, messages, options = {}) {
1029
1279
  }
1030
1280
 
1031
1281
  function parseCodexJsonlFileIntoMessages(filePath, messages, options = {}) {
1282
+ const readPath = _codexReadablePath(filePath);
1283
+ if (!readPath) {
1284
+ return { bytesRead: 0, completeBytesRead: 0, linesRead: 0, messagesAdded: 0, sessionMeta: null, failed: 1, compressed: true };
1285
+ }
1032
1286
  const state = {
1033
- seenUsers: options.seenUsers || new Set(),
1287
+ seenUsers: normalizeCodexUserDeduper(options.codexUserDeduper || options.seenUsers),
1034
1288
  sessionMeta: null,
1035
1289
  };
1036
1290
  const before = messages.length;
@@ -1038,12 +1292,13 @@ function parseCodexJsonlFileIntoMessages(filePath, messages, options = {}) {
1038
1292
  const buf = Buffer.alloc(chunkSize);
1039
1293
  const decoder = new StringDecoder('utf8');
1040
1294
  let bytesReadTotal = 0;
1295
+ let completeBytesRead = 0;
1041
1296
  let linesRead = 0;
1042
1297
  let partial = '';
1043
1298
  let fd = null;
1044
1299
 
1045
1300
  try {
1046
- fd = fs.openSync(filePath, 'r');
1301
+ fd = fs.openSync(readPath, 'r');
1047
1302
  while (true) {
1048
1303
  const bytesRead = fs.readSync(fd, buf, 0, buf.length, null);
1049
1304
  if (bytesRead <= 0) break;
@@ -1054,9 +1309,10 @@ function parseCodexJsonlFileIntoMessages(filePath, messages, options = {}) {
1054
1309
  const end = text.indexOf('\n', start);
1055
1310
  if (end === -1) break;
1056
1311
  const line = text.slice(start, end);
1312
+ completeBytesRead += Buffer.byteLength(line) + 1;
1057
1313
  if (line) {
1058
1314
  linesRead++;
1059
- parseCodexJsonlLine(line, messages, state);
1315
+ parseCodexJsonlRecord(line, messages, state);
1060
1316
  }
1061
1317
  start = end + 1;
1062
1318
  }
@@ -1066,7 +1322,8 @@ function parseCodexJsonlFileIntoMessages(filePath, messages, options = {}) {
1066
1322
  if (tail) partial += tail;
1067
1323
  if (partial && partial.trim()) {
1068
1324
  linesRead++;
1069
- parseCodexJsonlLine(partial, messages, state);
1325
+ const parsed = parseCodexJsonlRecord(partial, messages, state);
1326
+ if (parsed.valid) completeBytesRead += Buffer.byteLength(partial);
1070
1327
  }
1071
1328
  } finally {
1072
1329
  try { if (fd != null) fs.closeSync(fd); } catch {}
@@ -1074,6 +1331,78 @@ function parseCodexJsonlFileIntoMessages(filePath, messages, options = {}) {
1074
1331
 
1075
1332
  return {
1076
1333
  bytesRead: bytesReadTotal,
1334
+ completeBytesRead,
1335
+ linesRead,
1336
+ messagesAdded: messages.length - before,
1337
+ sessionMeta: state.sessionMeta,
1338
+ };
1339
+ }
1340
+
1341
+ async function parseCodexJsonlFileIntoMessagesAsync(filePath, messages, options = {}) {
1342
+ const state = {
1343
+ seenUsers: normalizeCodexUserDeduper(options.codexUserDeduper || options.seenUsers),
1344
+ sessionMeta: null,
1345
+ };
1346
+ const before = messages.length;
1347
+ const chunkSize = options.chunkSize || 1024 * 1024;
1348
+ const yieldAfterMs = Math.max(5, Number(options.yieldAfterMs || 25));
1349
+ const buf = Buffer.alloc(chunkSize);
1350
+ const decoder = new StringDecoder('utf8');
1351
+ let bytesReadTotal = 0;
1352
+ let completeBytesRead = 0;
1353
+ let linesRead = 0;
1354
+ let partial = '';
1355
+ let lastYieldAt = Date.now();
1356
+ let fh = null;
1357
+
1358
+ async function maybeYield() {
1359
+ if (Date.now() - lastYieldAt < yieldAfterMs) return;
1360
+ await new Promise((resolve) => setImmediate(resolve));
1361
+ lastYieldAt = Date.now();
1362
+ }
1363
+
1364
+ const readPath = _codexReadablePath(filePath);
1365
+ if (!readPath) {
1366
+ return { bytesRead: 0, completeBytesRead: 0, linesRead: 0, messagesAdded: 0, sessionMeta: null, failed: 1, compressed: true };
1367
+ }
1368
+ try {
1369
+ fh = await fs.promises.open(readPath, 'r');
1370
+ let position = 0;
1371
+ while (true) {
1372
+ const { bytesRead } = await fh.read(buf, 0, buf.length, position);
1373
+ if (bytesRead <= 0) break;
1374
+ position += bytesRead;
1375
+ bytesReadTotal += bytesRead;
1376
+ const text = partial + decoder.write(buf.subarray(0, bytesRead));
1377
+ let start = 0;
1378
+ while (true) {
1379
+ const end = text.indexOf('\n', start);
1380
+ if (end === -1) break;
1381
+ const line = text.slice(start, end);
1382
+ completeBytesRead += Buffer.byteLength(line) + 1;
1383
+ if (line) {
1384
+ linesRead++;
1385
+ parseCodexJsonlRecord(line, messages, state);
1386
+ }
1387
+ start = end + 1;
1388
+ }
1389
+ partial = text.slice(start);
1390
+ await maybeYield();
1391
+ }
1392
+ const tail = decoder.end();
1393
+ if (tail) partial += tail;
1394
+ if (partial && partial.trim()) {
1395
+ linesRead++;
1396
+ const parsed = parseCodexJsonlRecord(partial, messages, state);
1397
+ if (parsed.valid) completeBytesRead += Buffer.byteLength(partial);
1398
+ }
1399
+ } finally {
1400
+ try { if (fh) await fh.close(); } catch {}
1401
+ }
1402
+
1403
+ return {
1404
+ bytesRead: bytesReadTotal,
1405
+ completeBytesRead,
1077
1406
  linesRead,
1078
1407
  messagesAdded: messages.length - before,
1079
1408
  sessionMeta: state.sessionMeta,
@@ -1082,8 +1411,13 @@ function parseCodexJsonlFileIntoMessages(filePath, messages, options = {}) {
1082
1411
 
1083
1412
  function parseCodexJsonlTailFileIntoMessages(filePath, messages, options = {}) {
1084
1413
  const maxBytes = Math.max(1, Number(options.maxBytes || 10 * 1024 * 1024));
1414
+ // A compressed archive can't be range-read — tail the materialized copy.
1415
+ const readPath = _codexReadablePath(filePath);
1416
+ if (!readPath) {
1417
+ return { bytesRead: 0, linesRead: 0, messagesAdded: 0, fileSize: 0, partial: false, failed: 1, compressed: true };
1418
+ }
1085
1419
  let stat;
1086
- try { stat = fs.statSync(filePath); } catch {
1420
+ try { stat = fs.statSync(readPath); } catch {
1087
1421
  return { bytesRead: 0, linesRead: 0, messagesAdded: 0, fileSize: 0, partial: false, failed: 1 };
1088
1422
  }
1089
1423
  const size = Number(stat.size || 0);
@@ -1095,7 +1429,7 @@ function parseCodexJsonlTailFileIntoMessages(filePath, messages, options = {}) {
1095
1429
  const startOffset = Math.max(0, size - readSize);
1096
1430
  let fd = null;
1097
1431
  try {
1098
- fd = fs.openSync(filePath, 'r');
1432
+ fd = fs.openSync(readPath, 'r');
1099
1433
  const buf = Buffer.alloc(readSize);
1100
1434
  const bytesRead = fs.readSync(fd, buf, 0, readSize, startOffset);
1101
1435
  let content = buf.toString('utf8', 0, bytesRead);
@@ -1104,7 +1438,7 @@ function parseCodexJsonlTailFileIntoMessages(filePath, messages, options = {}) {
1104
1438
  content = nlIdx >= 0 ? content.slice(nlIdx + 1) : '';
1105
1439
  }
1106
1440
  const before = messages.length;
1107
- const parsed = parseCodexJsonlIntoMessages(content, messages, { seenUsers: options.seenUsers });
1441
+ const parsed = parseCodexJsonlIntoMessages(content, messages, { codexUserDeduper: options.codexUserDeduper || options.seenUsers });
1108
1442
  return {
1109
1443
  ...parsed,
1110
1444
  bytesRead,
@@ -1164,8 +1498,10 @@ function codexSearchMatchForMessage(msg, terms, order) {
1164
1498
  }
1165
1499
 
1166
1500
  function searchCodexRolloutFileMessages(filePath, terms, row) {
1167
- const state = { seenUsers: new Set(), sessionMeta: null };
1501
+ const state = { codexUserDeduper: createCodexUserDeduper(), sessionMeta: null };
1168
1502
  const matches = [];
1503
+ const readPath = _codexReadablePath(filePath);
1504
+ if (!readPath) return matches;
1169
1505
  const chunkSize = 1024 * 1024;
1170
1506
  const buf = Buffer.alloc(chunkSize);
1171
1507
  const decoder = new StringDecoder('utf8');
@@ -1174,7 +1510,7 @@ function searchCodexRolloutFileMessages(filePath, terms, row) {
1174
1510
  let order = 0;
1175
1511
 
1176
1512
  try {
1177
- fd = fs.openSync(filePath, 'r');
1513
+ fd = fs.openSync(readPath, 'r');
1178
1514
  while (true) {
1179
1515
  const bytesRead = fs.readSync(fd, buf, 0, buf.length, null);
1180
1516
  if (bytesRead <= 0) break;
@@ -1195,12 +1531,10 @@ function searchCodexRolloutFileMessages(filePath, terms, row) {
1195
1531
  const msg = codexMessageFromEntry(entry);
1196
1532
  if (msg) {
1197
1533
  if (msg.role === 'user') {
1198
- const key = codexUserKey(msg.text);
1199
- if (!key || state.seenUsers.has(key)) {
1534
+ if (!state.codexUserDeduper.remember(msg.text, msg.timestamp)) {
1200
1535
  start = end + 1;
1201
1536
  continue;
1202
1537
  }
1203
- state.seenUsers.add(key);
1204
1538
  }
1205
1539
  const match = codexSearchMatchForMessage(msg, terms, order++);
1206
1540
  if (match) matches.push(match);
@@ -1225,11 +1559,8 @@ function searchCodexRolloutFileMessages(filePath, terms, row) {
1225
1559
  if (msg) {
1226
1560
  let shouldConsider = true;
1227
1561
  if (msg.role === 'user') {
1228
- const key = codexUserKey(msg.text);
1229
- if (!key || state.seenUsers.has(key)) {
1562
+ if (!state.codexUserDeduper.remember(msg.text, msg.timestamp)) {
1230
1563
  shouldConsider = false;
1231
- } else {
1232
- state.seenUsers.add(key);
1233
1564
  }
1234
1565
  }
1235
1566
  const match = shouldConsider ? codexSearchMatchForMessage(msg, terms, order++) : null;
@@ -1305,6 +1636,11 @@ function mergeCodexRecentThreadRow(existing, incoming) {
1305
1636
  updated_at: Math.max(base.updated_at || 0, other.updated_at || 0),
1306
1637
  providerResumeId: base.providerResumeId || other.providerResumeId || '',
1307
1638
  providerResumeIds: [...providerResumeIds],
1639
+ thread_source: base.thread_source || other.thread_source || '',
1640
+ parent_agent_session_id: base.parent_agent_session_id || base.parent_thread_id || other.parent_agent_session_id || other.parent_thread_id || '',
1641
+ parent_thread_id: base.parent_thread_id || base.parent_agent_session_id || other.parent_thread_id || other.parent_agent_session_id || '',
1642
+ agent_nickname: base.agent_nickname || other.agent_nickname || '',
1643
+ agent_role: base.agent_role || other.agent_role || '',
1308
1644
  _jsonlPath: base._jsonlPath || other._jsonlPath || '',
1309
1645
  _fileSize: base._fileSize || other._fileSize || 0,
1310
1646
  _modifiedAt: base._modifiedAt || other._modifiedAt || '',
@@ -1331,6 +1667,11 @@ function normalizeCodexRecentThreadRows(rows, getRolloutInfo = () => ({})) {
1331
1667
  id: canonicalId,
1332
1668
  providerResumeId,
1333
1669
  providerResumeIds,
1670
+ thread_source: row.thread_source || '',
1671
+ parent_agent_session_id: row.parent_agent_session_id || row.parent_thread_id || '',
1672
+ parent_thread_id: row.parent_thread_id || row.parent_agent_session_id || '',
1673
+ agent_nickname: row.agent_nickname || '',
1674
+ agent_role: row.agent_role || '',
1334
1675
  _originalThreadId: originalThreadId,
1335
1676
  _jsonlPath: rolloutPath,
1336
1677
  _fileSize: rolloutInfo.fileSize || row._fileSize || 0,
@@ -1437,7 +1778,7 @@ function listCodexSessionsFromRollouts(homeDir = process.env.HOME, limit = 50, o
1437
1778
  for (const entry of entries) {
1438
1779
  const fullPath = path.join(dir, entry.name);
1439
1780
  if (entry.isDirectory()) { stack.push(fullPath); continue; }
1440
- if (!entry.isFile() || !entry.name.endsWith('.jsonl')) continue;
1781
+ if (!entry.isFile() || !isCodexRolloutCandidateName(entry.name)) continue;
1441
1782
  let row = _readCodexRolloutMetadata(fullPath);
1442
1783
  if (!row) continue;
1443
1784
  row = _codexRolloutRowWithFileInfo(row, fullPath);
@@ -1474,10 +1815,13 @@ module.exports = {
1474
1815
  cleanCodexUserText,
1475
1816
  codexInputText,
1476
1817
  codexMessageFromEntry,
1818
+ codexStructuredMessageFromEntry,
1819
+ isCodexRolloutCandidateName,
1477
1820
  codexRolloutFileInfo,
1478
1821
  codexRolloutFileMatchesThread,
1479
1822
  codexRolloutIdFromPath,
1480
1823
  codexUserKey,
1824
+ createCodexUserDeduper,
1481
1825
  checkCodexStateIntegrity,
1482
1826
  extractResumeTarget,
1483
1827
  findCodexSessionFiles,
@@ -1504,5 +1848,6 @@ module.exports = {
1504
1848
  parseSessionStartMs,
1505
1849
  parseCodexJsonlIntoMessages,
1506
1850
  parseCodexJsonlFileIntoMessages,
1851
+ parseCodexJsonlFileIntoMessagesAsync,
1507
1852
  parseCodexJsonlTailFileIntoMessages,
1508
1853
  };