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
@@ -15,12 +15,14 @@
15
15
 
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
+ const crypto = require('crypto');
18
19
  const claudeDesktopSessions = require('./lib/claude-desktop-sessions');
19
20
  const {
20
21
  codexRolloutIdFromPath,
21
22
  findCodexSessionFiles,
22
23
  findCodexSessionFilesByFilename,
23
24
  findCodexStateRolloutPath,
25
+ readCodexRolloutMetadata,
24
26
  } = require('./lib/session-history');
25
27
 
26
28
  const CLAUDE_PROJECTS_DIR = path.join(process.env.HOME, '.claude', 'projects');
@@ -64,6 +66,42 @@ function isCodexSessionRow(row) {
64
66
  || String(row?.jsonl_path || '').includes(`${path.sep}.codex${path.sep}sessions${path.sep}`);
65
67
  }
66
68
 
69
+ function providerChildThreadFields(row = {}) {
70
+ const threadSource = String(row.thread_source || row.threadSource || '').trim().toLowerCase();
71
+ const parentAgentSessionId = String(
72
+ row.parent_agent_session_id
73
+ || row.parentAgentSessionId
74
+ || row.parent_thread_id
75
+ || row.parentThreadId
76
+ || ''
77
+ ).trim();
78
+ return { threadSource, parentAgentSessionId };
79
+ }
80
+
81
+ function isProviderChildAgentRow(row = {}) {
82
+ const provider = String(row.provider || '').trim().toLowerCase();
83
+ const { threadSource, parentAgentSessionId } = providerChildThreadFields(row);
84
+ return provider === 'codex' && threadSource === 'subagent' && !!parentAgentSessionId;
85
+ }
86
+
87
+ function addAgentRowFileIds(dbFileIds, row = {}) {
88
+ if (row.id) dbFileIds.add(row.id);
89
+ if (row.agent_session_id) dbFileIds.add(row.agent_session_id);
90
+ const jsonlPath = String(row.jsonl_path || '').trim();
91
+ if (jsonlPath) {
92
+ const fileId = sessionFileIdFromPath(jsonlPath);
93
+ if (fileId) dbFileIds.add(fileId);
94
+ const rolloutId = codexRolloutIdFromPath(jsonlPath);
95
+ if (rolloutId) dbFileIds.add(rolloutId);
96
+ if (isCodexSessionRow(row)) {
97
+ try {
98
+ const meta = readCodexRolloutMetadata(jsonlPath) || {};
99
+ if (meta.id) dbFileIds.add(String(meta.id));
100
+ } catch {}
101
+ }
102
+ }
103
+ }
104
+
67
105
  function resolveCodexCanonicalFile(expectedFileId, fileIndex) {
68
106
  if (!expectedFileId) return null;
69
107
  if (fileIndex[expectedFileId]) return fileIndex[expectedFileId];
@@ -291,18 +329,31 @@ function detectMismatches(db, getAllSessionFiles) {
291
329
  // Check first and select accordingly.
292
330
  let hasSlugColumn = false;
293
331
  let hasProviderResumeColumn = false;
332
+ let hasThreadSourceColumn = false;
333
+ let hasParentAgentSessionColumn = false;
334
+ let hasAgentNicknameColumn = false;
335
+ let hasAgentRoleColumn = false;
294
336
  try {
295
337
  const cols = db.prepare('PRAGMA table_info(agent_sessions)').all();
296
338
  hasSlugColumn = cols.some((c) => c.name === 'slug');
297
339
  hasProviderResumeColumn = cols.some((c) => c.name === 'provider_resume_id');
340
+ hasThreadSourceColumn = cols.some((c) => c.name === 'thread_source');
341
+ hasParentAgentSessionColumn = cols.some((c) => c.name === 'parent_agent_session_id');
342
+ hasAgentNicknameColumn = cols.some((c) => c.name === 'agent_nickname');
343
+ hasAgentRoleColumn = cols.some((c) => c.name === 'agent_role');
298
344
  } catch {}
299
345
  const slugCol = hasSlugColumn ? 'a.slug' : "'' AS slug";
300
346
  const providerResumeCol = hasProviderResumeColumn ? 'a.provider_resume_id' : "'' AS provider_resume_id";
347
+ const threadSourceCol = hasThreadSourceColumn ? 'a.thread_source' : "'' AS thread_source";
348
+ const parentAgentSessionCol = hasParentAgentSessionColumn ? 'a.parent_agent_session_id' : "'' AS parent_agent_session_id";
349
+ const agentNicknameCol = hasAgentNicknameColumn ? 'a.agent_nickname' : "'' AS agent_nickname";
350
+ const agentRoleCol = hasAgentRoleColumn ? 'a.agent_role' : "'' AS agent_role";
301
351
  allSessions = db.prepare(`
302
352
  SELECT c.id, c.provider, c.title, c.user_renamed, c.starred, c.project_path, c.cwd,
303
353
  c.created_at, c.updated_at,
304
354
  a.agent_session_id, ${providerResumeCol}, a.jsonl_path, a.file_size, a.first_message,
305
355
  a.modified_at, a.hostname, a.model, a.git_branch, a.user_msg_count,
356
+ ${threadSourceCol}, ${parentAgentSessionCol}, ${agentNicknameCol}, ${agentRoleCol},
306
357
  ${slugCol}
307
358
  FROM ctm_sessions c
308
359
  LEFT JOIN agent_sessions a ON a.ctm_session_id = c.id
@@ -600,8 +651,7 @@ function detectMismatches(db, getAllSessionFiles) {
600
651
  // Check 6: Orphan files (on disk but not in DB)
601
652
  const dbFileIds = new Set();
602
653
  for (const row of allSessions) {
603
- dbFileIds.add(row.id);
604
- if (row.agent_session_id) dbFileIds.add(row.agent_session_id);
654
+ addAgentRowFileIds(dbFileIds, row);
605
655
  if (isCodexSessionRow(row) && row.agent_session_id) {
606
656
  try {
607
657
  for (const filePath of findCodexSessionFiles(row.agent_session_id)) {
@@ -611,6 +661,27 @@ function detectMismatches(db, getAllSessionFiles) {
611
661
  } catch {}
612
662
  }
613
663
  }
664
+ try {
665
+ const agentCols = new Set(db.prepare('PRAGMA table_info(agent_sessions)').all().map(c => c.name));
666
+ const threadSourceCol = agentCols.has('thread_source') ? 'thread_source' : "'' AS thread_source";
667
+ const parentAgentSessionCol = agentCols.has('parent_agent_session_id') ? 'parent_agent_session_id' : "'' AS parent_agent_session_id";
668
+ const allAgentRows = db.prepare(`
669
+ SELECT agent_session_id, ctm_session_id, provider, jsonl_path,
670
+ ${threadSourceCol}, ${parentAgentSessionCol}
671
+ FROM agent_sessions
672
+ `).all();
673
+ for (const row of allAgentRows) {
674
+ addAgentRowFileIds(dbFileIds, row);
675
+ if (isCodexSessionRow(row) && row.agent_session_id) {
676
+ try {
677
+ for (const filePath of findCodexSessionFiles(row.agent_session_id)) {
678
+ const rolloutFileId = sessionFileIdFromPath(filePath);
679
+ if (rolloutFileId) dbFileIds.add(rolloutFileId);
680
+ }
681
+ } catch {}
682
+ }
683
+ }
684
+ } catch {}
614
685
  for (const issue of issues) {
615
686
  if (issue.type === 'codex_agent_jsonl_mismatch' && issue.details?.file_agent_session_id) {
616
687
  dbFileIds.add(issue.details.file_agent_session_id);
@@ -706,25 +777,39 @@ function detectMismatches(db, getAllSessionFiles) {
706
777
  });
707
778
  }
708
779
 
709
- // Check 7: Orphan agent_sessions (no valid ctm_session parent)
710
- // Invariant: every agent_session MUST have a ctm_session parent.
711
- // Orphans indicate a bug watcher/reconciliation should always auto-create parents.
780
+ // Check 7: Orphan top-level agent_sessions (no valid ctm_session parent).
781
+ // Provider-native child threads (Codex subagents, etc.) are deliberately
782
+ // indexed as agent identities without top-level CTM tabs. Promoting them here
783
+ // creates the one-message "ghost sessions" that pollute Recent Sessions.
712
784
  try {
785
+ const agentCols = new Set(db.prepare('PRAGMA table_info(agent_sessions)').all().map(c => c.name));
786
+ const threadSourceCol = agentCols.has('thread_source') ? 'a.thread_source' : "'' AS thread_source";
787
+ const parentAgentSessionCol = agentCols.has('parent_agent_session_id') ? 'a.parent_agent_session_id' : "'' AS parent_agent_session_id";
788
+ const agentNicknameCol = agentCols.has('agent_nickname') ? 'a.agent_nickname' : "'' AS agent_nickname";
789
+ const agentRoleCol = agentCols.has('agent_role') ? 'a.agent_role' : "'' AS agent_role";
713
790
  const orphanAgents = db.prepare(`
714
- SELECT a.agent_session_id, a.ctm_session_id, a.project_path, a.jsonl_path, a.file_size
791
+ SELECT a.agent_session_id, a.ctm_session_id, a.provider, a.project_path,
792
+ a.jsonl_path, a.file_size,
793
+ ${threadSourceCol}, ${parentAgentSessionCol}, ${agentNicknameCol}, ${agentRoleCol}
715
794
  FROM agent_sessions a
716
795
  LEFT JOIN ctm_sessions c ON c.id = a.ctm_session_id
717
796
  WHERE a.ctm_session_id IS NULL OR c.id IS NULL
718
797
  `).all();
719
798
  for (const row of orphanAgents) {
799
+ if (isProviderChildAgentRow(row)) continue;
720
800
  issues.push({
721
801
  type: 'orphan_agent_session', severity: 'warning', sessionId: row.agent_session_id,
722
802
  details: {
723
803
  agent_session_id: row.agent_session_id,
724
804
  stale_ctm_id: row.ctm_session_id || null,
805
+ provider: row.provider || '',
725
806
  project_path: row.project_path || '',
726
807
  jsonl_path: row.jsonl_path || '',
727
808
  file_size: row.file_size || 0,
809
+ thread_source: row.thread_source || '',
810
+ parent_agent_session_id: row.parent_agent_session_id || '',
811
+ agent_nickname: row.agent_nickname || '',
812
+ agent_role: row.agent_role || '',
728
813
  },
729
814
  suggestion: 'agent_session has no valid ctm_session parent. Will auto-create parent.',
730
815
  });
@@ -1082,9 +1167,15 @@ function recoverMismatches(db, issues, getAllSessionFiles) {
1082
1167
 
1083
1168
  case 'orphan_agent_session': {
1084
1169
  // Auto-create a ctm_session parent for orphaned agent_sessions.
1085
- // This restores the invariant: every agent_session has a parent.
1170
+ // This restores the invariant for top-level agent_sessions. Provider
1171
+ // child threads are intentionally parentless from CTM's tab model.
1086
1172
  const agentId = issue.details.agent_session_id;
1087
1173
  if (!agentId) { result.skipped++; break; }
1174
+ if (isProviderChildAgentRow(issue.details || {})) {
1175
+ result.skipped++;
1176
+ result.actions.push(`Skipped provider child agent ${agentId.slice(0, 8)}; child threads are not CTM tabs`);
1177
+ break;
1178
+ }
1088
1179
  try {
1089
1180
  const txn = db.transaction(() => {
1090
1181
  // Create a new ctm_session using the agent_session_id as the CTM id
@@ -1277,12 +1368,16 @@ function repairSafeRelinkSwaps(db, auditResult = null) {
1277
1368
  const projectA = rowA.project_path || stA.cwd || ctmRowA.cwd || '';
1278
1369
  const projectB = rowB.project_path || stB.cwd || ctmRowB.cwd || '';
1279
1370
  _updateColumns(db, 'startup_tasks', 'ctm_session_id', ctmA, {
1371
+ agent_session_id: desiredA,
1372
+ claude_session_id: desiredA,
1280
1373
  agent_project_dir: _dirForJsonl(rowA.jsonl_path),
1281
1374
  claude_project_dir: _dirForJsonl(rowA.jsonl_path),
1282
1375
  cwd: projectA,
1283
1376
  worktree_path: /\/\.(?:claude|walle)\/worktrees\//.test(projectA) ? projectA : null,
1284
1377
  });
1285
1378
  _updateColumns(db, 'startup_tasks', 'ctm_session_id', ctmB, {
1379
+ agent_session_id: desiredB,
1380
+ claude_session_id: desiredB,
1286
1381
  agent_project_dir: _dirForJsonl(rowB.jsonl_path),
1287
1382
  claude_project_dir: _dirForJsonl(rowB.jsonl_path),
1288
1383
  cwd: projectB,
@@ -1320,6 +1415,11 @@ const INTEGRITY_ISSUE_TELEMETRY_TTL_MS = 6 * 60 * 60 * 1000;
1320
1415
  const INTEGRITY_ISSUE_TELEMETRY_DETAIL_CAP = 5;
1321
1416
  const _lastIntegrityIssueTelemetry = new Map();
1322
1417
 
1418
+ function integrityDetailTelemetryEnabled() {
1419
+ const value = String(process.env.CTM_SESSION_INTEGRITY_DETAIL_TELEMETRY || '').toLowerCase();
1420
+ return value === '1' || value === 'true' || value === 'yes';
1421
+ }
1422
+
1323
1423
  function integrityIssueTelemetryKey(issue) {
1324
1424
  const details = issue && issue.details && typeof issue.details === 'object'
1325
1425
  ? Object.keys(issue.details).sort().join('|')
@@ -1349,7 +1449,7 @@ function integrityIssueTelemetryDetails(issue) {
1349
1449
  ? issue.details
1350
1450
  : {};
1351
1451
  const detailKeys = Object.keys(details).sort();
1352
- const detailsHash = require('crypto')
1452
+ const detailsHash = crypto
1353
1453
  .createHash('sha256')
1354
1454
  .update(JSON.stringify(details))
1355
1455
  .digest('hex')
@@ -1360,8 +1460,40 @@ function integrityIssueTelemetryDetails(issue) {
1360
1460
  };
1361
1461
  }
1362
1462
 
1463
+ function integrityIssueTelemetrySummary(issues) {
1464
+ const byType = {};
1465
+ const bySeverity = {};
1466
+ const sampleHashes = [];
1467
+ const seenHashes = new Set();
1468
+ for (const issue of issues || []) {
1469
+ const type = issue?.type || 'unknown';
1470
+ const severity = issue?.severity || 'unknown';
1471
+ byType[type] = (byType[type] || 0) + 1;
1472
+ bySeverity[severity] = (bySeverity[severity] || 0) + 1;
1473
+ if (sampleHashes.length < 10) {
1474
+ const details = integrityIssueTelemetryDetails(issue);
1475
+ const key = `${type}:${severity}:${details.details_hash}`;
1476
+ if (!seenHashes.has(key)) {
1477
+ seenHashes.add(key);
1478
+ sampleHashes.push({
1479
+ type,
1480
+ severity,
1481
+ detail_keys: details.detail_keys,
1482
+ details_hash: details.details_hash,
1483
+ });
1484
+ }
1485
+ }
1486
+ }
1487
+ return {
1488
+ issue_count: (issues || []).length,
1489
+ by_type: JSON.stringify(byType),
1490
+ by_severity: JSON.stringify(bySeverity),
1491
+ sample_hashes: JSON.stringify(sampleHashes),
1492
+ };
1493
+ }
1494
+
1363
1495
  /**
1364
- * Report integrity check results to telemetry with full debug context.
1496
+ * Report integrity check results to telemetry with bounded debug context.
1365
1497
  */
1366
1498
  function reportToTelemetry(telemetry, issues, recovery) {
1367
1499
  if (!telemetry || typeof telemetry.track !== 'function') return;
@@ -1382,16 +1514,24 @@ function reportToTelemetry(telemetry, issues, recovery) {
1382
1514
  recovery_skipped: recovery ? recovery.skipped : 0,
1383
1515
  });
1384
1516
 
1385
- // Detail events for errors and warnings (not info — too noisy)
1517
+ // One low-cardinality issue summary keeps fleet visibility without emitting
1518
+ // one telemetry row per mismatched session. Detailed issue rows are opt-in for
1519
+ // local debugging because old fleets made this event type very noisy.
1386
1520
  const serious = issues.filter(i => i.severity === 'error' || i.severity === 'critical');
1387
- for (const issue of serious.filter(issue => shouldReportIntegrityIssue(issue)).slice(0, INTEGRITY_ISSUE_TELEMETRY_DETAIL_CAP)) {
1388
- const detailMeta = integrityIssueTelemetryDetails(issue);
1389
- telemetry.track('session_integrity_issue', {
1390
- type: issue.type,
1391
- severity: issue.severity,
1392
- session_id: issue.sessionId ? issue.sessionId.slice(0, 8) : null,
1393
- ...detailMeta,
1394
- });
1521
+ if (serious.length > 0) {
1522
+ telemetry.track('session_integrity_issue_summary', integrityIssueTelemetrySummary(serious));
1523
+ }
1524
+
1525
+ if (integrityDetailTelemetryEnabled()) {
1526
+ for (const issue of serious.filter(issue => shouldReportIntegrityIssue(issue)).slice(0, INTEGRITY_ISSUE_TELEMETRY_DETAIL_CAP)) {
1527
+ const detailMeta = integrityIssueTelemetryDetails(issue);
1528
+ telemetry.track('session_integrity_issue', {
1529
+ type: issue.type,
1530
+ severity: issue.severity,
1531
+ session_id: issue.sessionId ? issue.sessionId.slice(0, 8) : null,
1532
+ ...detailMeta,
1533
+ });
1534
+ }
1395
1535
  }
1396
1536
 
1397
1537
  // Recovery actions
@@ -1521,6 +1661,8 @@ module.exports = {
1521
1661
  integrityIssueTelemetryKey,
1522
1662
  shouldReportIntegrityIssue,
1523
1663
  integrityIssueTelemetryDetails,
1664
+ integrityIssueTelemetrySummary,
1665
+ integrityDetailTelemetryEnabled,
1524
1666
  resetIntegrityIssueTelemetry: () => _lastIntegrityIssueTelemetry.clear(),
1525
1667
  },
1526
1668
  };
@@ -59,6 +59,7 @@ function searchMatchFieldPriority(hit) {
59
59
  const field = String(hit?._matchField || '');
60
60
  if (field === 'session_id') return 100;
61
61
  if (hit?._isExactTitle) return 95;
62
+ if (field === 'session_reference') return 93;
62
63
  if (field === 'message' && hit?._messageStrong) return 92;
63
64
  if (field === 'title') return 90;
64
65
  if (field === 'message') return 80;
@@ -61,6 +61,12 @@ function messageText(messageOrContent) {
61
61
  return walleTranscript.extractText(content);
62
62
  }
63
63
 
64
+ function shouldSkipLegacyWalleSessionFile(file, fileSet) {
65
+ const sessionId = String(file || '').replace(/\.jsonl(\.bak)?$/, '');
66
+ if (!sessionId || sessionId.startsWith('walle-')) return false;
67
+ return fileSet.has(`walle-${sessionId}.jsonl`) || fileSet.has(`walle-${sessionId}.jsonl.bak`);
68
+ }
69
+
64
70
  function pushWalleSessionFiles(results) {
65
71
  const sessionsDir = walleTranscript.defaultSessionsDir(process.env);
66
72
  let files;
@@ -69,6 +75,7 @@ function pushWalleSessionFiles(results) {
69
75
  for (const file of files) {
70
76
  if (!file.endsWith('.jsonl') && !file.endsWith('.jsonl.bak')) continue;
71
77
  if (file.endsWith('.jsonl.bak') && fileSet.has(file.replace(/\.bak$/, ''))) continue;
78
+ if (shouldSkipLegacyWalleSessionFile(file, fileSet)) continue;
72
79
  const filePath = path.join(sessionsDir, file);
73
80
  let stat;
74
81
  try { stat = fs.statSync(filePath); } catch { continue; }
@@ -123,6 +130,7 @@ async function pushWalleSessionFilesAsync(results) {
123
130
  for (const file of files) {
124
131
  if (!file.endsWith('.jsonl') && !file.endsWith('.jsonl.bak')) continue;
125
132
  if (file.endsWith('.jsonl.bak') && fileSet.has(file.replace(/\.bak$/, ''))) continue;
133
+ if (shouldSkipLegacyWalleSessionFile(file, fileSet)) continue;
126
134
  const filePath = path.join(sessionsDir, file);
127
135
  let stat;
128
136
  try { stat = await fsp.stat(filePath); } catch { continue; }
@@ -175,20 +183,27 @@ function parseSessionFile(filePath, projectPath, projectEntry) {
175
183
  const meta = readCodexRolloutMetadata(filePath) || {};
176
184
  const cwd = meta.cwd || projectPath || path.dirname(filePath);
177
185
  const modelId = meta.model || '';
186
+ const firstMessage = meta.first_user_message || meta.title || '';
187
+ const title = firstMessage
188
+ ? firstMessage.split('\n')[0].replace(/^#+\s*/, '').replace(/[*_`]/g, '').trim().slice(0, 80)
189
+ : '';
190
+ const createdAt = meta.created_at
191
+ ? new Date(Number(meta.created_at) * 1000).toISOString()
192
+ : modifiedAt;
178
193
  return {
179
194
  sessionId: meta.id || codexSessionId,
180
195
  project: cwd,
181
196
  projectEntry: 'codex',
182
197
  cwd,
183
- firstMessage: '',
184
- lastUserContent: '',
198
+ firstMessage,
199
+ lastUserContent: firstMessage,
185
200
  firstAssistantText: '',
186
201
  renameName: '',
187
- title: '',
202
+ title,
188
203
  isEmpty: false,
189
- userMsgCount: 0,
204
+ userMsgCount: firstMessage ? 1 : 0,
190
205
  modifiedAt,
191
- timestamp: meta.timestamp || modifiedAt,
206
+ timestamp: createdAt,
192
207
  version: '',
193
208
  gitBranch: meta.git_branch || '',
194
209
  slug: '',
@@ -199,6 +214,11 @@ function parseSessionFile(filePath, projectPath, projectEntry) {
199
214
  model: modelId,
200
215
  agent: 'codex',
201
216
  jsonlPath: filePath,
217
+ threadSource: meta.thread_source || '',
218
+ parentAgentSessionId: meta.parent_agent_session_id || meta.parent_thread_id || '',
219
+ parentThreadId: meta.parent_thread_id || meta.parent_agent_session_id || '',
220
+ agentNickname: meta.agent_nickname || '',
221
+ agentRole: meta.agent_role || '',
202
222
  };
203
223
  }
204
224
 
@@ -46,6 +46,14 @@ const PATTERNS = [
46
46
  { re: />\s*\/dev\/[sh]d[a-z]/, reason: 'direct write to block device', category: 'destruction' },
47
47
  { re: />\s*\/etc\//, reason: 'direct overwrite of /etc/', category: 'destruction' },
48
48
  { re: />\s*\/boot\//, reason: 'direct overwrite of /boot/', category: 'destruction' },
49
+ { re: />\s*\/usr\//, reason: 'direct overwrite of /usr/', category: 'destruction' },
50
+ { re: />\s*\/var\//, reason: 'direct overwrite of /var/', category: 'destruction' },
51
+
52
+ // --- Irreversible data / history loss (seeded so allow-by-default can't run them) ---
53
+ { re: /\bdrop\s+(table|database|schema)\b/i, reason: 'SQL drop table/database', category: 'destruction' },
54
+ { re: /\btruncate\s+table\b/i, reason: 'SQL truncate table', category: 'destruction' },
55
+ { re: /\bgit\s+push\b[^\n]*\s(--force|-f)\b/, reason: 'git push --force (history overwrite)', category: 'destruction' },
56
+ { re: /\bchmod\s+[0-7]*777\b[^\n]*(\/(?:etc|usr|var|boot|bin|sbin|lib)\b|\s\/(?:\s|$)|~)/, reason: 'chmod 777 on a system path', category: 'perms' },
49
57
 
50
58
  // --- Credential exfiltration / secret leakage ---
51
59
  { re: /\b(curl|wget|nc|ncat|netcat)\b.*\.(ssh|gnupg|aws|kube|config)\b/i, reason: 'credential exfil via network tool', category: 'exfiltration' },
@@ -58,6 +66,7 @@ const PATTERNS = [
58
66
  { re: /\beval\s+.*`/, reason: 'eval with backtick substitution', category: 'exec' },
59
67
  { re: /\b(curl|wget)\s+.*\|\s*(bash|sh|zsh|source)\b/, reason: 'curl|bash (remote code exec)', category: 'exec' },
60
68
  { re: /\b(bash|sh|zsh)\s+<\s*\(.*\b(curl|wget)\b/, reason: 'process substitution remote exec', category: 'exec' },
69
+ { re: /\bfind\b[\s\S]*\s-exec\s+(rm|rmdir|unlink|shred|dd|mkfs|sh|bash|zsh|chmod|chown|mv|truncate)\b/, reason: 'find -exec running a destructive command', category: 'exec' },
61
70
 
62
71
  // --- Recursive permission / ownership on sensitive paths ---
63
72
  { re: /\bchmod\s+-[a-zA-Z]*R[a-zA-Z]*\s+\S+\s+(\/(?:\s|$)|~\/|\/etc(?:\s|\/|$)|\/var(?:\s|\/|$)|\/home(?:\s|\/|$))/, reason: 'recursive chmod on sensitive path', category: 'perms' },
@@ -101,29 +110,108 @@ function _normalizeCommand(input) {
101
110
  try { return String(input); } catch { return ''; }
102
111
  }
103
112
 
113
+ // --- User-configurable patterns (Permission tab) -------------------------
114
+ // The built-in PATTERNS above are sensible DEFAULTS shipped with CTM. The user
115
+ // can, in the Permission tab: (a) DISABLE specific defaults (by id), and (b) ADD
116
+ // their own custom patterns. Both live in the `approval_blocklist_config` setting
117
+ // and are passed into checkBlocklist() by the approval agent. Custom regexes are
118
+ // user-authored on their own machine — still, we validate + ReDoS-guard them so a
119
+ // fat-fingered pattern can't wedge the approval path.
120
+ const CUSTOM_FLAGS_ALLOWED = 'imsu';
121
+ const CUSTOM_SOURCE_MAX = 200;
122
+ const CUSTOM_PATTERNS_MAX = 200;
123
+
124
+ // ReDoS guard — same shape as approval-agent's isSafeRegex (nested quantifiers
125
+ // are the main catastrophic-backtracking vector). Kept local so the worker has
126
+ // no cross-module dependency.
127
+ function _isSafeRegexSource(source) {
128
+ if (/(\+|\*|\{)\)?(\+|\*|\?)/.test(source)) return false; // nested quantifiers
129
+ if ((source.match(/\|/g) || []).length > 20) return false; // excessive alternation
130
+ if (/\|\||^\||\|$/.test(source)) return false; // empty alternation → matches all
131
+ return true;
132
+ }
133
+
134
+ // Validate one user pattern. Returns { ok, re?, normalized?, error? }. Pure — the
135
+ // API layer uses it to reject bad input with a message; checkBlocklist uses it to
136
+ // skip (not throw on) a stored pattern that somehow became invalid.
137
+ function validateUserPattern(p) {
138
+ if (!p || typeof p !== 'object') return { ok: false, error: 'pattern must be an object' };
139
+ const source = typeof p.source === 'string' ? p.source.trim() : '';
140
+ if (!source) return { ok: false, error: 'regex is required' };
141
+ if (source.length > CUSTOM_SOURCE_MAX) return { ok: false, error: `regex too long (max ${CUSTOM_SOURCE_MAX} chars)` };
142
+ const flags = typeof p.flags === 'string' ? p.flags : '';
143
+ for (const f of flags) if (!CUSTOM_FLAGS_ALLOWED.includes(f)) return { ok: false, error: `unsupported flag "${f}" (allowed: ${CUSTOM_FLAGS_ALLOWED})` };
144
+ if (new Set(flags).size !== flags.length) return { ok: false, error: 'duplicate regex flag' };
145
+ if (!_isSafeRegexSource(source)) return { ok: false, error: 'regex rejected (nested quantifiers / excessive alternation — possible ReDoS)' };
146
+ let re;
147
+ try { re = new RegExp(source, flags); } catch (e) { return { ok: false, error: `invalid regex: ${e.message}` }; }
148
+ return {
149
+ ok: true,
150
+ re,
151
+ normalized: {
152
+ source,
153
+ flags,
154
+ reason: String(p.reason || '').slice(0, 200).trim() || 'Custom blocklist pattern',
155
+ category: (String(p.category || 'custom').slice(0, 40).trim() || 'custom'),
156
+ },
157
+ };
158
+ }
159
+
160
+ // Coerce a raw config object into the shape checkBlocklist expects, dropping
161
+ // anything malformed. Returns { disabledIds: Set<number>, customPatterns: [{re, reason, category, id}] }.
162
+ function _resolveConfig(config) {
163
+ const disabledIds = new Set();
164
+ const customPatterns = [];
165
+ if (config && typeof config === 'object') {
166
+ const raw = Array.isArray(config.disabledIds) ? config.disabledIds : [];
167
+ for (const id of raw) { const n = Number(id); if (Number.isInteger(n) && n >= 0 && n < PATTERNS.length) disabledIds.add(n); }
168
+ const customs = Array.isArray(config.customPatterns) ? config.customPatterns.slice(0, CUSTOM_PATTERNS_MAX) : [];
169
+ customs.forEach((p, i) => {
170
+ const v = validateUserPattern(p);
171
+ if (v.ok) customPatterns.push({ re: v.re, reason: v.normalized.reason, category: v.normalized.category, id: (p && p.id != null) ? p.id : `c${i}` });
172
+ });
173
+ }
174
+ return { disabledIds, customPatterns };
175
+ }
176
+
104
177
  // Check a command against the blocklist.
105
178
  // Returns { blocked: true, reason, category, patternId } on first match,
106
179
  // or { blocked: false } otherwise.
107
180
  //
108
- // This is synchronous and O(N patterns). All patterns are anchored on
109
- // literal keywords (\brm, \bmkfs, etc.) so there's no catastrophic backtracking
110
- // concern even on adversarial input.
111
- function checkBlocklist(command) {
181
+ // Optional `config` ({ disabledIds, customPatterns }) layers the user's Permission
182
+ // tab edits on top of the defaults: disabled default ids are skipped, and custom
183
+ // patterns are checked after the defaults. Omitting `config` evaluates the defaults
184
+ // only (back-compat). Default matches carry a numeric `patternId`; custom matches
185
+ // carry `custom: true` + `customId` and `patternId: null`.
186
+ //
187
+ // This is synchronous and O(N patterns). The built-in patterns are anchored on
188
+ // literal keywords (\brm, \bmkfs, etc.) and custom patterns are ReDoS-guarded, so
189
+ // there's no catastrophic backtracking concern even on adversarial input.
190
+ function checkBlocklist(command, config) {
112
191
  const cmd = _normalizeCommand(command);
113
192
  if (!cmd) return { blocked: false };
193
+ const { disabledIds, customPatterns } = _resolveConfig(config);
194
+ // 1) Built-in defaults (skip user-disabled ids).
114
195
  for (let i = 0; i < PATTERNS.length; i++) {
196
+ if (disabledIds.has(i)) continue;
115
197
  const p = PATTERNS[i];
116
198
  if (p.re.test(cmd)) {
117
199
  return { blocked: true, reason: p.reason, category: p.category, patternId: i };
118
200
  }
119
201
  }
202
+ // 2) User custom patterns.
203
+ for (const c of customPatterns) {
204
+ if (c.re.test(cmd)) {
205
+ return { blocked: true, reason: c.reason, category: c.category, patternId: null, custom: true, customId: c.id };
206
+ }
207
+ }
120
208
  return { blocked: false };
121
209
  }
122
210
 
123
211
  // Legacy helper — check multiple strings at once (e.g. tool name + command).
124
- function checkBlocklistMulti(fields) {
212
+ function checkBlocklistMulti(fields, config) {
125
213
  for (const v of fields) {
126
- const res = checkBlocklist(v);
214
+ const res = checkBlocklist(v, config);
127
215
  if (res.blocked) return res;
128
216
  }
129
217
  return { blocked: false };
@@ -132,7 +220,9 @@ function checkBlocklistMulti(fields) {
132
220
  module.exports = {
133
221
  checkBlocklist,
134
222
  checkBlocklistMulti,
223
+ validateUserPattern,
135
224
  PATTERN_META,
225
+ DEFAULT_PATTERN_COUNT: PATTERNS.length,
136
226
  // Exposed for tests — callers should not mutate.
137
227
  _PATTERNS: PATTERNS,
138
228
  };
@@ -26,7 +26,8 @@ const ABOVE_ANCHOR_DEPTH = 40;
26
26
 
27
27
  // Footer anchor that real widgets render at the bottom of the prompt.
28
28
  // Claude Code: "Esc to cancel". Codex: "Press enter to confirm or esc to cancel".
29
- const ANCHOR_RE = /Esc to cancel|esc to cancel|Press enter to confirm/;
29
+ // Cursor Agent: "Skip (esc or n)".
30
+ const ANCHOR_RE = /Esc to cancel|esc to cancel|Press enter to confirm|\(esc or n\)/;
30
31
 
31
32
  // Approval-option pattern. Accepts an optional selection-arrow prefix in any of
32
33
  // the forms different CLIs use: ❯ (Claude Code), ›/▶/▸ (Cursor/others), or
@@ -35,6 +36,11 @@ const ANCHOR_RE = /Esc to cancel|esc to cancel|Press enter to confirm/;
35
36
  // — which is unstyled in Codex's renderer and trips no-widget-formatting.
36
37
  const YES_RE = /^\s*(?:[❯›▶▸>]\s*)?\d+\.\s*(?:Yes|Allow)\b/i;
37
38
 
39
+ // Cursor Agent's "Run this command?" widget has no numbered options — the
40
+ // confirm row is a hotkey: "→ Run (once) (y)". Recognize it as the structural
41
+ // "Yes" row so the validator accepts Cursor prompts.
42
+ const CURSOR_RUN_RE = /^\s*(?:[▸❯›▶→>-]+\s*)?Run\s*\(once\)\s*\(y\)/i;
43
+
38
44
  /**
39
45
  * Check if the terminal is currently displaying an active approval widget.
40
46
  *
@@ -108,7 +114,7 @@ function validateWidget(term, opts = {}) {
108
114
  const line = buf.getLine(buf.viewportY + i);
109
115
  if (!line) continue;
110
116
  const text = line.translateToString(true);
111
- if (YES_RE.test(text)) { yesRow = i; break; }
117
+ if (YES_RE.test(text) || CURSOR_RUN_RE.test(text)) { yesRow = i; break; }
112
118
  }
113
119
  if (yesRow < 0) {
114
120
  return { valid: false, reason: 'yes-not-at-bottom' };
@@ -140,7 +146,7 @@ function _hasWidgetFormatting(buf, yesRow, totalRows) {
140
146
  const yesLine = buf.getLine(buf.viewportY + yesRow);
141
147
  if (!yesLine) return false;
142
148
  const yesText = yesLine.translateToString(true);
143
- if (/[❯›▶▸]/.test(yesText)) return true;
149
+ if (/[❯›▶▸→]/.test(yesText)) return true;
144
150
 
145
151
  // Check for a selection marker near the approval options. Codex MCP forms can
146
152
  // select option 2 ("Allow for this session"), while option 1 is the first
@@ -149,17 +155,17 @@ function _hasWidgetFormatting(buf, yesRow, totalRows) {
149
155
  const line = buf.getLine(buf.viewportY + row);
150
156
  if (!line) continue;
151
157
  const text = line.translateToString(true);
152
- // "❯" (U+276F), "›" (U+203A), "▶" (U+25B6), ">" surrounded by spaces
153
- if (/[❯›▶▸]/.test(text)) return true;
158
+ // "❯" (U+276F), "›" (U+203A), "▶" (U+25B6), "" (U+2192, Cursor), ">" spaced
159
+ if (/[❯›▶▸→]/.test(text)) return true;
154
160
  }
155
161
 
156
- // Check for "❯" marker anywhere in bottom 5 rows for prompts whose option
157
- // block is pushed down by long wrapped content.
162
+ // Check for a selection marker anywhere in bottom 5 rows for prompts whose
163
+ // option block is pushed down by long wrapped content.
158
164
  for (let row = Math.max(0, totalRows - 5); row < totalRows; row++) {
159
165
  const line = buf.getLine(buf.viewportY + row);
160
166
  if (!line) continue;
161
167
  const text = line.translateToString(true);
162
- if (/[❯›▶▸]/.test(text)) return true;
168
+ if (/[❯›▶▸→]/.test(text)) return true;
163
169
  }
164
170
 
165
171
  // Check for ANSI foreground color on the Yes-option line.