create-walle 0.10.1 → 0.10.2

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 (341) hide show
  1. package/README.md +4 -0
  2. package/lib/owner-write-safety.js +135 -0
  3. package/lib/public-runtime.js +13 -5
  4. package/lib/release-schema.json +3 -2
  5. package/lib/runtime-closure-policy.json +6 -4
  6. package/package.json +1 -1
  7. package/template/bin/deps-doctor.js +1 -1
  8. package/template/bin/node-bin.sh +11 -0
  9. package/template/coding-task-manager/api-prompts.js +10 -22
  10. package/template/coding-task-manager/bin/claude-hook.js +7 -3
  11. package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
  12. package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
  13. package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
  14. package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
  15. package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
  16. package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
  17. package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
  18. package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
  19. package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
  20. package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
  21. package/template/coding-task-manager/db.js +705 -286
  22. package/template/coding-task-manager/git-utils.js +44 -0
  23. package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
  24. package/template/coding-task-manager/lib/agent-presets.js +15 -0
  25. package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
  26. package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
  27. package/template/coding-task-manager/lib/api/host-stats.js +98 -0
  28. package/template/coding-task-manager/lib/api/ingress.js +35 -12
  29. package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
  30. package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
  31. package/template/coding-task-manager/lib/api/machines.js +461 -0
  32. package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
  33. package/template/coding-task-manager/lib/api/providers.js +53 -6
  34. package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
  35. package/template/coding-task-manager/lib/api/sessions.js +160 -8
  36. package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
  37. package/template/coding-task-manager/lib/api/worktrees.js +38 -37
  38. package/template/coding-task-manager/lib/approval-drift.js +16 -1
  39. package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
  40. package/template/coding-task-manager/lib/approval-scope.js +68 -4
  41. package/template/coding-task-manager/lib/auth-context.js +32 -2
  42. package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
  43. package/template/coding-task-manager/lib/auth-rules.js +186 -45
  44. package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
  45. package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
  46. package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
  47. package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
  48. package/template/coding-task-manager/lib/claude-session-title.js +6 -30
  49. package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
  50. package/template/coding-task-manager/lib/client-diag-handler.js +5 -0
  51. package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
  52. package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
  53. package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
  54. package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
  55. package/template/coding-task-manager/lib/codex-paths.js +2 -1
  56. package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
  57. package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
  58. package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
  59. package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
  60. package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
  61. package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
  62. package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
  63. package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
  64. package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
  65. package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
  66. package/template/coding-task-manager/lib/db-domains.js +21 -2
  67. package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
  68. package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
  69. package/template/coding-task-manager/lib/feature-manifest.js +1 -1
  70. package/template/coding-task-manager/lib/fs-watcher.js +3 -2
  71. package/template/coding-task-manager/lib/harness-history-transition.js +3 -3
  72. package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
  73. package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
  74. package/template/coding-task-manager/lib/headless-term-service.js +55 -17
  75. package/template/coding-task-manager/lib/host-control-dial.js +167 -0
  76. package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
  77. package/template/coding-task-manager/lib/host-handoff.js +51 -13
  78. package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
  79. package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
  80. package/template/coding-task-manager/lib/host-stats.js +328 -0
  81. package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
  82. package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
  83. package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
  84. package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
  85. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
  86. package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
  87. package/template/coding-task-manager/lib/instance-manifest.js +13 -1
  88. package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
  89. package/template/coding-task-manager/lib/launch-model-override.js +160 -1
  90. package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
  91. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
  92. package/template/coding-task-manager/lib/lead-model-switch.js +3 -2
  93. package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
  94. package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
  95. package/template/coding-task-manager/lib/login-tripwire.js +1 -0
  96. package/template/coding-task-manager/lib/machine-pairing.js +238 -0
  97. package/template/coding-task-manager/lib/machine-pool.js +409 -0
  98. package/template/coding-task-manager/lib/machine-registry.js +749 -0
  99. package/template/coding-task-manager/lib/machine-self.js +66 -0
  100. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +52 -4
  101. package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
  102. package/template/coding-task-manager/lib/media-journal.js +84 -27
  103. package/template/coding-task-manager/lib/media-store-options.js +43 -15
  104. package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
  105. package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
  106. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  107. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  108. package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
  109. package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
  110. package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
  111. package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
  112. package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
  113. package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
  114. package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
  115. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  116. package/template/coding-task-manager/lib/native-history-control.js +2 -2
  117. package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
  118. package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
  119. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
  120. package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
  121. package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
  122. package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
  123. package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
  124. package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
  125. package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
  126. package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
  127. package/template/coding-task-manager/lib/read-pool-client.js +5 -2
  128. package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
  129. package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
  130. package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
  131. package/template/coding-task-manager/lib/remote-command-context.js +12 -0
  132. package/template/coding-task-manager/lib/remote-command-runtime.js +13 -1
  133. package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
  134. package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
  135. package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
  136. package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
  137. package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
  138. package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
  139. package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
  140. package/template/coding-task-manager/lib/restart-preflight.js +9 -2
  141. package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
  142. package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
  143. package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
  144. package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
  145. package/template/coding-task-manager/lib/resume-cwd.js +13 -0
  146. package/template/coding-task-manager/lib/resume-from-store.js +12 -1
  147. package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
  148. package/template/coding-task-manager/lib/resume-preflight.js +21 -16
  149. package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
  150. package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
  151. package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
  152. package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
  153. package/template/coding-task-manager/lib/root-prompt-admission.js +33 -2
  154. package/template/coding-task-manager/lib/root-prompt-flow.js +167 -33
  155. package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
  156. package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
  157. package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
  158. package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
  159. package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
  160. package/template/coding-task-manager/lib/session-create/identity.js +20 -1
  161. package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
  162. package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
  163. package/template/coding-task-manager/lib/session-create/provider-process.js +34 -37
  164. package/template/coding-task-manager/lib/session-create/response.js +30 -12
  165. package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
  166. package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
  167. package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
  168. package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
  169. package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
  170. package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
  171. package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
  172. package/template/coding-task-manager/lib/session-host-manager.js +152 -206
  173. package/template/coding-task-manager/lib/session-jobs.js +17 -9
  174. package/template/coding-task-manager/lib/session-lifecycle.js +18 -0
  175. package/template/coding-task-manager/lib/session-model-action-preconditions.js +9 -0
  176. package/template/coding-task-manager/lib/session-resource-sampler.js +19 -9
  177. package/template/coding-task-manager/lib/session-standup.js +67 -4
  178. package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
  179. package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
  180. package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
  181. package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
  182. package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
  183. package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
  184. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
  185. package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
  186. package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
  187. package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
  188. package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
  189. package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
  190. package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
  191. package/template/coding-task-manager/lib/transcript-link.js +77 -0
  192. package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
  193. package/template/coding-task-manager/lib/transcript-source.js +8 -3
  194. package/template/coding-task-manager/lib/transcript-store.js +40 -0
  195. package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
  196. package/template/coding-task-manager/lib/typing-interaction-trace.js +92 -0
  197. package/template/coding-task-manager/lib/typing-worker-stages.js +60 -0
  198. package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
  199. package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
  200. package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
  201. package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
  202. package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
  203. package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
  204. package/template/coding-task-manager/lib/ws-router.js +68 -1
  205. package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
  206. package/template/coding-task-manager/oauth-proxy.js +99 -55
  207. package/template/coding-task-manager/package-lock.json +24 -0
  208. package/template/coding-task-manager/package.json +3 -0
  209. package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
  210. package/template/coding-task-manager/providers/codex-mcp.js +14 -5
  211. package/template/coding-task-manager/public/css/app.css +279 -1
  212. package/template/coding-task-manager/public/css/reviews.css +4 -0
  213. package/template/coding-task-manager/public/index.html +294 -223
  214. package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
  215. package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
  216. package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
  217. package/template/coding-task-manager/public/js/background-work.js +13 -7
  218. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
  219. package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
  220. package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
  221. package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
  222. package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
  223. package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
  224. package/template/coding-task-manager/public/js/history-scroller.js +187 -9
  225. package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
  226. package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
  227. package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
  228. package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -8
  229. package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
  230. package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
  231. package/template/coding-task-manager/public/js/reviews.js +7 -3
  232. package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
  233. package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
  234. package/template/coding-task-manager/public/js/row-join.js +4 -2
  235. package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
  236. package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
  237. package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
  238. package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
  239. package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
  240. package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
  241. package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
  242. package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
  243. package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
  244. package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
  245. package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
  246. package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
  247. package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
  248. package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -1
  249. package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
  250. package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
  251. package/template/coding-task-manager/public/js/walle-session.js +30 -7
  252. package/template/coding-task-manager/public/m/app.css +226 -8
  253. package/template/coding-task-manager/public/m/app.js +502 -9
  254. package/template/coding-task-manager/public/m/index.html +38 -1
  255. package/template/coding-task-manager/public/m/machines.js +1313 -0
  256. package/template/coding-task-manager/public/m/pool-failover.js +391 -0
  257. package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
  258. package/template/coding-task-manager/public/m/sw.js +5 -0
  259. package/template/coding-task-manager/queue-engine.js +13 -0
  260. package/template/coding-task-manager/server/approvals.js +5 -6
  261. package/template/coding-task-manager/server/boot.js +136 -17
  262. package/template/coding-task-manager/server.js +902 -239
  263. package/template/coding-task-manager/session-integrity.js +7 -9
  264. package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
  265. package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
  266. package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
  267. package/template/coding-task-manager/workers/session-host-process.js +14 -3
  268. package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
  269. package/template/coding-task-manager/workers/worktree-worker.js +34 -0
  270. package/template/package-lock.json +2 -2
  271. package/template/package.json +1 -1
  272. package/template/scripts/conversation-storage-v2-migration.js +78 -11
  273. package/template/shared/conversation-storage-v2-contract.js +42 -6
  274. package/template/shared/fixtures/worker-status-payloads.json +392 -0
  275. package/template/shared/git-remote-key.js +83 -0
  276. package/template/shared/host-identity.js +98 -2
  277. package/template/shared/owner-write-safety.js +135 -0
  278. package/template/shared/public-runtime.js +13 -5
  279. package/template/shared/release-schema.json +3 -2
  280. package/template/shared/report-citations.js +14 -0
  281. package/template/shared/sqlite-write-lock.js +361 -223
  282. package/template/shared/storage/contract.js +35 -3
  283. package/template/wall-e/agent.js +21 -0
  284. package/template/wall-e/api-walle.js +5 -0
  285. package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
  286. package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
  287. package/template/wall-e/brain/helper-rules-schema.js +129 -0
  288. package/template/wall-e/brain/migrations.js +51 -22
  289. package/template/wall-e/brain/model-deployment-facts.js +31 -1
  290. package/template/wall-e/brain/owner-call-whitelist.js +16 -0
  291. package/template/wall-e/brain/schema-ensures.js +22 -0
  292. package/template/wall-e/brain.js +1 -1
  293. package/template/wall-e/coding/journal-dispatcher.js +51 -0
  294. package/template/wall-e/coding/session-journal.js +28 -40
  295. package/template/wall-e/coding-orchestrator.js +23 -26
  296. package/template/wall-e/http/auth.js +18 -1
  297. package/template/wall-e/http/blog-views-api.js +318 -0
  298. package/template/wall-e/http/helper-rules-api.js +321 -0
  299. package/template/wall-e/http/model-proxy-providers.js +3 -1
  300. package/template/wall-e/http/telemetry-api.js +12 -0
  301. package/template/wall-e/lib/blog-view-policy.js +88 -0
  302. package/template/wall-e/lib/blog-view-store.js +169 -0
  303. package/template/wall-e/lib/client-config-lock.js +2 -1
  304. package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
  305. package/template/wall-e/lib/error-kinds.js +10 -1
  306. package/template/wall-e/lib/feature-manifest.js +1 -1
  307. package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
  308. package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
  309. package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
  310. package/template/wall-e/lib/helper-rules/grade.js +277 -0
  311. package/template/wall-e/lib/helper-rules/label.js +247 -0
  312. package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
  313. package/template/wall-e/lib/helper-rules/store.js +667 -0
  314. package/template/wall-e/lib/mcp-integration.js +5 -6
  315. package/template/wall-e/lib/runtime-worker-pool.js +1 -0
  316. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
  317. package/template/wall-e/lib/telemetry-db-schema.js +37 -0
  318. package/template/wall-e/lib/telemetry-process-client.js +76 -1
  319. package/template/wall-e/llm/default-fallback.js +4 -0
  320. package/template/wall-e/llm/model-catalog.js +13 -4
  321. package/template/wall-e/llm/model-roles.js +6 -0
  322. package/template/wall-e/llm/portkey-http.js +150 -0
  323. package/template/wall-e/llm/portkey-sync.js +15 -145
  324. package/template/wall-e/llm/worker-mcp-routing.js +61 -0
  325. package/template/wall-e/llm/worker-route.js +43 -1
  326. package/template/wall-e/loops/brain-custom-writes.js +45 -0
  327. package/template/wall-e/mcp-protocol-contract.js +41 -1
  328. package/template/wall-e/mcp-server.js +13 -2
  329. package/template/wall-e/mcp-worker-schemas.js +20 -2
  330. package/template/wall-e/memory/ctm-context-client.js +25 -5
  331. package/template/wall-e/shared/release-schema.json +3 -2
  332. package/template/wall-e/shared/sqlite-write-lock.js +361 -223
  333. package/template/wall-e/shared/storage/contract.js +35 -3
  334. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  335. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
  336. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
  337. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
  338. package/template/wall-e/tools/local-tools.js +6 -4
  339. package/template/wall-e/workers/brain-owner-worker.js +9 -0
  340. package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
  341. package/template/wall-e/workers/telemetry-worker.js +8 -0
@@ -53,7 +53,6 @@ const http = require('node:http');
53
53
  const os = require('node:os');
54
54
  const path = require('node:path');
55
55
  const { execFile } = require('node:child_process');
56
- const readline = require('node:readline');
57
56
  const { hostIdentityAccessAllowed } = require('./lib/host-identity-access');
58
57
 
59
58
  const { effectiveOutputCap } = require('./lib/model-proxy/output-cap');
@@ -73,6 +72,9 @@ const { getDataDir } = require('./lib/data-dir');
73
72
  const { validateLeadWorkerTarget, safeRefusalReason } = require('./lib/model-proxy/lead-request-validation');
74
73
  const { buildCodexModelsResponse } = require('./lib/harness-model-profile');
75
74
  const legacyRelay = require('./lib/model-proxy/legacy-relay');
75
+ const streamAccountingMod = require('./lib/model-proxy/stream-accounting');
76
+ const instanceRecord = require('./lib/model-proxy/proxy-instance-record');
77
+ const handoff = require('./lib/model-proxy/handoff');
76
78
  const {
77
79
  unsupportedSafeguards, logReviewDiagnostic,
78
80
  } = require('./lib/model-proxy/auto-mode-review');
@@ -88,6 +90,10 @@ const EBIND_NOT_LOOPBACK = 'EBIND_NOT_LOOPBACK';
88
90
  // `_health` answer reports it, and it is intentionally NOT per-instance.
89
91
  let _abortedStreams = 0;
90
92
 
93
+ // The context of the server this process started. Read only by the CLI's SIGTERM handler (bottom of
94
+ // this file) — an embedded caller installs no such handler, so nothing else can observe it.
95
+ let _cliExitTarget = null;
96
+
91
97
  // ── per-instance context (WP-1 §5 D-3, review item 2) ───────────────────────────────────────────
92
98
  //
93
99
  // Every start() captures its OWN immutable context: the admin secret, the OAuth resolver, the native
@@ -120,6 +126,14 @@ function createProxyContext({ adminSecret = null, resolveOauthToken, dataDir, na
120
126
  // registration and closed by (and only by) this instance's wrapped HTTP server close. It holds the
121
127
  // loaded authority's private material and never exposes it.
122
128
  nativeListener: null,
129
+ // §5 D-9: THIS server's in-flight stream counter, created here so both the plaintext and the native
130
+ // server feed one number, and so a test can reach it through the context.
131
+ streamAccounting: streamAccountingMod.createStreamAccounting(),
132
+ // §5 D-7 (WP-2): what a handoff hands over -- the listening server, its ports, its generation.
133
+ httpServer: null,
134
+ boundPort: null,
135
+ nativePort: null,
136
+ generation: 1,
123
137
  };
124
138
  }
125
139
 
@@ -319,10 +333,15 @@ async function _handleAdmin(req, res, pathName, ctx) {
319
333
  // servers each authenticate against their own secret, so A's admin surface can never be opened with
320
334
  // B's secret (and B's credential resolver can never be reached through A).
321
335
  if (!ctx.adminSecret || typeof suppliedSecret !== 'string' || suppliedSecret !== ctx.adminSecret) {
336
+ // §9 S-9: one line, the finite reason only — never the supplied header value.
337
+ console.log('[model-proxy] refused status=401 reason=admin_unauthorized');
322
338
  res.writeHead(401, { 'Content-Type': 'application/json' });
323
339
  res.end(JSON.stringify({ error: 'admin_unauthorized' }));
324
340
  return;
325
341
  }
342
+ // §5 D-8 (WP-3): this authenticated request -- and only this -- is "admin contact". `/_health`,
343
+ // `/_status` and a refused secret never reach it, so none of them can hold this proxy open.
344
+ handoff.noteAdminContact();
326
345
 
327
346
  const probeMatch = pathName.match(ADMIN_PROBE_RE);
328
347
  if (probeMatch && req.method === 'GET') {
@@ -364,6 +383,26 @@ async function _handleAdmin(req, res, pathName, ctx) {
364
383
  }
365
384
  }
366
385
 
386
+ // §5 D-7 (WP-2): upgrade by handoff. The successor is always spawned from THIS file's path on disk and
387
+ // the body may carry the two bounded deadlines (test overrides), never a script or a port; a 409
388
+ // leaves this proxy serving exactly as it was.
389
+ if (pathName === '/_admin/handoff' && req.method === 'POST') {
390
+ const result = await handoff.initiateHandoff({
391
+ ctx, httpServer: ctx.httpServer, nativeListener: ctx.nativeListener, dataDir: ctx.dataDir,
392
+ port: ctx.boundPort, nativePort: ctx.nativePort, adminSecret: ctx.adminSecret,
393
+ generation: ctx.generation,
394
+ readyDeadlineMs: body ? body.readyDeadlineMs : undefined,
395
+ drainDeadlineMs: body ? body.drainDeadlineMs : undefined,
396
+ });
397
+ res.writeHead(result.ok ? 202 : 409, { 'Content-Type': 'application/json' });
398
+ res.end(JSON.stringify(result.ok
399
+ ? { ok: true, generation: result.generation, successorPid: result.successorPid, handoffMs: result.handoffMs }
400
+ : { ok: false, reason: result.reason }));
401
+ // The 202 is on the wire BEFORE step 7 is armed, so its exit can never preempt the caller's response.
402
+ if (result.ok) result.armDrain();
403
+ return;
404
+ }
405
+
367
406
  const workerLeaseMatch = pathName.match(ADMIN_WORKER_LEASE_RE);
368
407
  if (workerLeaseMatch && req.method === 'POST') {
369
408
  if (!body || !Number.isSafeInteger(body.generation) || !Number.isSafeInteger(body.lease)) {
@@ -779,6 +818,8 @@ async function _handleRouted(req, res, pathName, trusted = null, ctx = null) {
779
818
  : null);
780
819
  const authz = tokens.authorize(token, alias);
781
820
  if (!authz.ok) {
821
+ // §9 S-9: the same finite reason the body carries — never the presented token.
822
+ console.log(`[model-proxy] refused status=401 reason=token ${authz.reason}`);
782
823
  faceModule.writeJson(res, 401, faceModule.errorEnvelope('authentication_error', `token ${authz.reason}`));
783
824
  return;
784
825
  }
@@ -788,6 +829,7 @@ async function _handleRouted(req, res, pathName, trusted = null, ctx = null) {
788
829
  // keychain the incident was about. (The reverse direction — a legacy-bearer owner on the TLS lane —
789
830
  // is refused by the listener's carrier check.)
790
831
  if (!nativeTrusted && tokens.leadTokenNativeTransport(token) === tokens.NATIVE_TRANSPORT_MTLS) {
832
+ console.log('[model-proxy] refused status=401 reason=token native_transport_mismatch'); // §9 S-9
791
833
  faceModule.writeJson(res, 401, faceModule.errorEnvelope('authentication_error', 'token native_transport_mismatch'));
792
834
  return;
793
835
  }
@@ -1305,6 +1347,11 @@ async function handleRequest(req, res, trusted = null, ctx = null) {
1305
1347
  if (pathName.startsWith('/r/')) {
1306
1348
  return _handleRouted(req, res, pathName, trusted, context);
1307
1349
  }
1350
+ if (pathName === '/_health' || pathName === '/_status') {
1351
+ // WP-0 §9 S-8: the relay still builds its own fields; the wrapper merges this process's identity
1352
+ // and live stream count into that same JSON body.
1353
+ return _legacyHandleRequest(req, streamAccountingMod.withHealthFields(res, context.streamAccounting));
1354
+ }
1308
1355
  // Legacy: today's OAuth passthrough, byte-for-byte unchanged (AC-0a).
1309
1356
  return _legacyHandleRequest(req, res);
1310
1357
  }
@@ -1319,34 +1366,6 @@ async function _handleNativeAuthorizedRequest(req, res, { token, binding, suffix
1319
1366
  return _handleRouted(req, res, pathName, { native: true, token, binding, suffix, query }, ctx);
1320
1367
  }
1321
1368
 
1322
- // ── D-4 stdin secret (CLI mode only) ────────────────────────────────────
1323
- // Reads exactly one line from stdin at process start. Never env, never argv.
1324
- // Resolves null (no admin secret; admin endpoints stay locked) if stdin is a
1325
- // TTY or closes without producing a line.
1326
- function _readAdminSecretFromStdin() {
1327
- return new Promise((resolve) => {
1328
- if (!process.stdin || process.stdin.isTTY) {
1329
- resolve(null);
1330
- return;
1331
- }
1332
- const rl = readline.createInterface({ input: process.stdin, terminal: false });
1333
- let resolved = false;
1334
- const finish = (value) => {
1335
- if (resolved) return;
1336
- resolved = true;
1337
- rl.close();
1338
- resolve(value);
1339
- };
1340
- rl.once('line', (line) => finish((line || '').trim() || null));
1341
- rl.once('close', () => finish(null));
1342
- });
1343
- }
1344
-
1345
- function _resolveHost() {
1346
- const raw = process.env.OAUTH_PROXY_HOST;
1347
- return typeof raw === 'string' && raw.length > 0 ? raw : HOST;
1348
- }
1349
-
1350
1369
  // D-1: bind 127.0.0.1 only. Throws a named error (never a bare listen()
1351
1370
  // failure) so both start() callers and the CLI entry can refuse cleanly.
1352
1371
  function _assertLoopbackHost(host) {
@@ -1357,7 +1376,7 @@ function _assertLoopbackHost(host) {
1357
1376
  }
1358
1377
  }
1359
1378
 
1360
- function start({ port = PORT, host = HOST, adminSecret = null, resolveOauthToken, dataDir, nativeUpstreamRequest, nativeLoadAuthority } = {}) {
1379
+ function start({ port = PORT, host = HOST, adminSecret = null, resolveOauthToken, dataDir, nativeUpstreamRequest, nativeLoadAuthority, inherited = null, lingerMs = null } = {}) {
1361
1380
  try {
1362
1381
  _assertLoopbackHost(host);
1363
1382
  } catch (err) {
@@ -1369,6 +1388,7 @@ function start({ port = PORT, host = HOST, adminSecret = null, resolveOauthToken
1369
1388
  // and gets lib/data-dir's resolver (CTM_DATA_DIR, then WALL_E_DATA_DIR, then the default). None of it
1370
1389
  // is ever read from a request, and none of it is shared with another live server.
1371
1390
  const ctx = createProxyContext({ adminSecret, resolveOauthToken, dataDir, nativeUpstreamRequest, nativeLoadAuthority });
1391
+ _cliExitTarget = ctx;
1372
1392
  const server = http.createServer((req, res) => {
1373
1393
  handleRequest(req, res, null, ctx).catch((err) => {
1374
1394
  try {
@@ -1379,24 +1399,31 @@ function start({ port = PORT, host = HOST, adminSecret = null, resolveOauthToken
1379
1399
  } catch {}
1380
1400
  });
1381
1401
  });
1402
+ // §5 D-9: this server's plaintext requests feed the same counter the exit record reads.
1403
+ ctx.streamAccounting.track(server);
1382
1404
  server.on('error', (err) => {
1383
1405
  console.error('[oauth-proxy] server error:', err.message);
1384
1406
  });
1385
1407
  return new Promise((resolve, reject) => {
1386
1408
  server.once('error', reject);
1387
- server.listen(port, host, () => {
1409
+ const onListening = () => {
1388
1410
  // The native port is derived from the ACTUAL bound port (never the requested one), so a lease of
1389
1411
  // 0 or a config mismatch cannot desynchronize the pair the fixture reserved.
1390
1412
  const boundPort = server.address().port;
1391
1413
  try {
1392
1414
  // This instance's OWN controller, closed by this instance's wrapped server close. A second live
1393
- // server has its own, so neither can serve the other's registrations.
1415
+ // server has its own, so neither can serve the other's registrations. §5 D-7: a successor binds
1416
+ // the TLS listening socket it was handed instead of the port (S-6), so the native lane has no
1417
+ // re-bind window either.
1394
1418
  ctx.nativeListener = createNativeLeadListener({
1395
1419
  httpPort: boundPort,
1396
1420
  dataDir: ctx.dataDir,
1421
+ // §5 D-9: the native lane is the same process carrying the same requests — one counter.
1422
+ streamAccounting: ctx.streamAccounting,
1397
1423
  // The default is the unowned library loader; a host test fixture may inject a gated loader to
1398
1424
  // force the admin readiness race at the real HTTP boundary (review item 9).
1399
1425
  loadAuthority: ctx.nativeLoadAuthority || undefined,
1426
+ inheritedHandle: inherited ? inherited.nativeHandle : null,
1400
1427
  onAuthorizedRequest: (req, res, payload) => _handleNativeAuthorizedRequest(req, res, payload, ctx),
1401
1428
  });
1402
1429
  // Closing the HTTP server starts the native close immediately (see the wrapper for why the
@@ -1409,9 +1436,42 @@ function start({ port = PORT, host = HOST, adminSecret = null, resolveOauthToken
1409
1436
  ctx.nativeListener = null;
1410
1437
  console.error(`[oauth-proxy] native lead listener unavailable: ${(err && err.code) || 'native_lead_listener_input_invalid'}`);
1411
1438
  }
1439
+ // §5 D-7 step 4: the state that has no other authority arrives with the sockets and is registered
1440
+ // BEFORE this process answers anything -- the token table and the per-token last lead model.
1441
+ if (inherited && inherited.state) {
1442
+ tokens.importAll(inherited.state.tokens);
1443
+ leadWorkerPicks.importLastLeadModel(inherited.state.lastLeadModel);
1444
+ }
1412
1445
  console.log(`[oauth-proxy] listening on http://${host}:${port}`);
1413
- resolve(server);
1414
- });
1446
+ // §5 D-2/D-3: publish this process's durable identity and hand the same closure to /_health. A
1447
+ // successor publishes generation n+1 and the pid it took over from, so the record names the owner.
1448
+ const record = instanceRecord.publishInstanceRecord({
1449
+ dataDir: ctx.dataDir,
1450
+ port: boundPort,
1451
+ nativePort: ctx.nativeListener ? boundPort + 1 : null,
1452
+ accounting: ctx.streamAccounting,
1453
+ generation: inherited ? inherited.generation : 1,
1454
+ predecessorPid: inherited ? inherited.predecessorPid : null,
1455
+ });
1456
+ ctx.httpServer = server;
1457
+ ctx.boundPort = boundPort;
1458
+ ctx.nativePort = ctx.nativeListener ? boundPort + 1 : null;
1459
+ ctx.generation = record.generation;
1460
+ // §5 D-8: the CLI arms the proxy's own linger once the record is published; an embedded start()
1461
+ // passes no lingerMs, so it can never exit the process that called it.
1462
+ if (lingerMs) handoff.armLinger({ lingerMs, ctx });
1463
+ if (!inherited) {
1464
+ resolve(server);
1465
+ return;
1466
+ }
1467
+ // §5 D-7 step 5: `ready` is the predecessor's permission to stop accepting, so handoff.js sends it
1468
+ // only once this process is genuinely serving (state imported, identity published, TLS re-bound).
1469
+ handoff.announceInheritedStart({ ctx, inherited, record }).then(() => resolve(server));
1470
+ };
1471
+ // §5 D-7 step 4: a successor binds the listening socket it was handed, never (port, host) again --
1472
+ // there is no moment in which the port has no owner.
1473
+ if (inherited && inherited.httpHandle) server.listen(inherited.httpHandle, onListening);
1474
+ else server.listen(port, host, onListening);
1415
1475
  });
1416
1476
  }
1417
1477
 
@@ -1431,26 +1491,10 @@ module.exports = {
1431
1491
  // failure so exactly-once finalization and listener cleanup are provable without a live relay.
1432
1492
  _relayLegacyObserved: legacyRelay._relayLegacyObserved,
1433
1493
  EBIND_NOT_LOOPBACK,
1494
+ // WP-2 test seam: the context of the server THIS process started (null for an embedded caller that
1495
+ // never ran the CLI block). proxy-cli's SIGTERM handler reads it; nothing else may.
1496
+ _startedContext: () => _cliExitTarget,
1434
1497
  };
1435
1498
 
1436
- // CLI mode: `node oauth-proxy.js`
1437
- if (require.main === module) {
1438
- const host = _resolveHost();
1439
- if (host !== '127.0.0.1') {
1440
- // D-1: refuse before ever touching stdin, so a bad --host/env doesn't
1441
- // hang waiting for an admin secret that will never matter.
1442
- console.error(`[oauth-proxy] ${EBIND_NOT_LOOPBACK}: refusing to bind to "${host}" — must be 127.0.0.1 (D-1)`);
1443
- process.exit(78);
1444
- }
1445
- _readAdminSecretFromStdin()
1446
- .catch(() => null)
1447
- .then((adminSecret) => start({ host, adminSecret }))
1448
- .catch((err) => {
1449
- if (err && err.code === EBIND_NOT_LOOPBACK) {
1450
- console.error(`[oauth-proxy] ${err.code}: ${err.message}`);
1451
- process.exit(78);
1452
- }
1453
- console.error('[oauth-proxy] failed to start:', err.message);
1454
- process.exit(1);
1455
- });
1456
- }
1499
+ // CLI mode: `node oauth-proxy.js` (or a handoff successor, spawned with an IPC channel).
1500
+ if (require.main === module) require('./lib/model-proxy/proxy-cli').runCli();
@@ -28,10 +28,13 @@
28
28
  "proxy-agent": "8.0.2",
29
29
  "puppeteer": "25.14.0",
30
30
  "qrcode": "^1.5.4",
31
+ "stream-chain": "4.2.6",
32
+ "stream-json": "3.7.0",
31
33
  "strip-ansi": "^6.0.1",
32
34
  "tree-sitter-bash": "^0.25.1",
33
35
  "web-push": "^3.6.7",
34
36
  "web-tree-sitter": "^0.26.13",
37
+ "wrap-ansi": "^9.0.2",
35
38
  "ws": "8.22.0",
36
39
  "yauzl": "3.4.0"
37
40
  },
@@ -3517,6 +3520,27 @@
3517
3520
  "node": ">= 0.8"
3518
3521
  }
3519
3522
  },
3523
+ "node_modules/stream-chain": {
3524
+ "version": "4.2.6",
3525
+ "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-4.2.6.tgz",
3526
+ "integrity": "sha512-1zeJ8CrtJfmiba26ui8jXkq/xLRFvhzkdH02D5QLO9Cnovgeb28IJxg88DdraWnjnkbOol/++uIAybJbJhk7ig==",
3527
+ "license": "BSD-3-Clause",
3528
+ "funding": {
3529
+ "url": "https://github.com/sponsors/uhop"
3530
+ }
3531
+ },
3532
+ "node_modules/stream-json": {
3533
+ "version": "3.7.0",
3534
+ "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-3.7.0.tgz",
3535
+ "integrity": "sha512-rCSBdcBP/bPk6T8QFcxAj1MSzAuc5i49cYW6IE7sYObNEPccBJIUiL6fU9c9BWt40aJK0siVynLX7lWaW8alXw==",
3536
+ "license": "BSD-3-Clause",
3537
+ "dependencies": {
3538
+ "stream-chain": "^4.2.5"
3539
+ },
3540
+ "funding": {
3541
+ "url": "https://github.com/sponsors/uhop"
3542
+ }
3543
+ },
3520
3544
  "node_modules/string_decoder": {
3521
3545
  "version": "1.3.0",
3522
3546
  "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -87,10 +87,13 @@
87
87
  "puppeteer": "25.14.0",
88
88
  "proxy-agent": "8.0.2",
89
89
  "qrcode": "^1.5.4",
90
+ "stream-chain": "4.2.6",
91
+ "stream-json": "3.7.0",
90
92
  "strip-ansi": "^6.0.1",
91
93
  "tree-sitter-bash": "^0.25.1",
92
94
  "web-push": "^3.6.7",
93
95
  "web-tree-sitter": "^0.26.13",
96
+ "wrap-ansi": "^9.0.2",
94
97
  "ws": "8.22.0",
95
98
  "yauzl": "3.4.0"
96
99
  },
@@ -1,33 +1,135 @@
1
1
  'use strict';
2
2
 
3
3
  // Shared extraction of the (server, tool) pair from Codex MCP approval prompts.
4
+ //
5
+ // Codex builds the approval question with
6
+ // format!("Allow {actor} to run tool \"{tool_name}\"?")
7
+ // (codex-rs/core/src/mcp_tool_call.rs), where `actor` is one of
8
+ // - "the <server> MCP server" — an ordinary MCP server (e.g. "the wall-e MCP server"),
9
+ // - "this app" — the built-in app/connector server, connector unnamed,
10
+ // - the connector's display name, e.g. "Google Drive" — also server `codex_apps`.
4
11
  // Two on-screen shapes carry the verb:
5
- // "Calling wall-e.walle_memory_status({})" → CALLING_RE
6
- // 'Allow the wall-e MCP server to run tool "X"?' → QUESTION_RE
12
+ // 'Allow <actor> to run tool "<tool>"?' → parseApprovalQuestion
13
+ // "Calling <qualified>" (with or without "({...})") → parseCallingEcho
14
+ //
15
+ // The actor is matched STRUCTURALLY: "the <x> MCP server" / "this app" /
16
+ // anything else. It is never checked against a list of connector names — new
17
+ // connectors must not need a code change to be recognized.
7
18
  //
8
- // Reused by providers/codex-mcp.js (the specific handler) AND providers/codex.js
9
- // (the generic fallback). This keeps the MCP verb visible even when the specific
10
- // codex-mcp parser doesn't fire — e.g. a viewport-wrapped prompt that drops the
11
- // "enter to submit" anchor falls back to codex.js, which would otherwise emit a
12
- // bare toolName='MCP' with no verb, hiding read-only calls (search_graph, get_*)
13
- // from the read/write classifier and stranding them on a manual banner.
19
+ // Reused by providers/codex-mcp.js (the specific handler), providers/codex.js
20
+ // (the generic fallback) AND workers/state-detectors/codex.js. This keeps the
21
+ // MCP verb visible even when the specific codex-mcp parser doesn't fire — e.g. a
22
+ // viewport-wrapped prompt that drops the "enter to submit" anchor falls back to
23
+ // codex.js, which would otherwise emit a bare toolName='MCP' with no verb,
24
+ // hiding read-only calls (search_graph, get_*) from the read/write classifier
25
+ // and stranding them on a manual banner.
26
+
27
+ // The built-in app/connector MCP server id (codex-rs/codex-mcp/src/mcp/mod.rs).
28
+ const CODEX_APPS_SERVER = 'codex_apps';
29
+
30
+ // 'Allow <actor> to run tool "<tool>"?' → the two captures. Anchored at the line
31
+ // start (that guards against prose that merely mentions the question) but tolerant
32
+ // of leading UI decoration: Codex's ratatui borders/footers paint box, bullet and
33
+ // cursor glyphs before the text ("│ Allow …, "• Calling …").
34
+ const APPROVAL_QUESTION_RE = /^[\s│┃║|•◦›❯>*-]*Allow\s+(.+?)\s+to run tool\s+["']?([A-Za-z0-9_.-]+)["']?\s*\?/i;
35
+ // Ordinary MCP servers name themselves in the actor: "the <server> MCP server".
36
+ // Case-insensitive so it agrees with APPROVAL_QUESTION_RE, which accepts "Allow".
37
+ const MCP_SERVER_ACTOR_RE = /^the (\S+) MCP server$/i;
38
+ // The unnamed connector actor.
39
+ const APP_FALLBACK_ACTOR = 'this app';
40
+ // "Calling <qualified>" — a DOTTED token ("server.tool"), as Codex always renders
41
+ // it. The "({...})" argument list is optional (and absent in current builds), but
42
+ // the dot is not: a bare "Calling foo" is prose, not an MCP approval signal.
43
+ const CALLING_ECHO_RE = /\bCalling\s+([A-Za-z0-9_-]+\.[A-Za-z0-9_.-]+)/i;
14
44
 
15
- const CALLING_RE = /\bCalling\s+([A-Za-z0-9_.-]+)\.([A-Za-z0-9_.-]+)\s*\(/i;
16
- const QUESTION_RE = /Allow the ([A-Za-z0-9_.-]+) MCP server to run tool ["']?([A-Za-z0-9_.-]+)["']?\?/i;
45
+ // 'Allow <actor> to run tool "<tool>"?' → { actorKind, actor, tool } | null
46
+ // actorKind: 'mcp_server' when actor matches MCP_SERVER_ACTOR_RE (the server
47
+ // name is that capture),
48
+ // 'app_fallback' when actor === 'this app',
49
+ // 'connector' otherwise (any non-empty text).
50
+ function parseApprovalQuestion(line) {
51
+ const match = String(line || '').match(APPROVAL_QUESTION_RE);
52
+ if (!match) return null;
53
+ const actor = match[1].trim();
54
+ const tool = match[2];
55
+ if (!actor || !tool) return null;
56
+ const actorKind = MCP_SERVER_ACTOR_RE.test(actor)
57
+ ? 'mcp_server'
58
+ : (actor.toLowerCase() === APP_FALLBACK_ACTOR ? 'app_fallback' : 'connector');
59
+ return { actorKind, actor, tool };
60
+ }
61
+
62
+ // 'Calling <qualified>' with or without a following '(' → { qualified } | null
63
+ // qualified = the dotted token after "Calling " ([A-Za-z0-9_-]+\.[A-Za-z0-9_.-]+),
64
+ // e.g. 'codex_apps.google_drive.update_file'. A dotless "Calling foo" is null.
65
+ function parseCallingEcho(line) {
66
+ const match = String(line || '').match(CALLING_ECHO_RE);
67
+ if (!match) return null;
68
+ return { qualified: match[1] };
69
+ }
70
+
71
+ // The server for a question whose actor does not name it: the connector/app
72
+ // server is `codex_apps`, but a "Calling" echo above the prompt can still spell
73
+ // out the qualified name — 'codex_apps.google_drive.update_file' with tool
74
+ // 'google_drive.update_file' → 'codex_apps'.
75
+ function serverForQuestion(question, rows) {
76
+ if (question.actorKind === 'mcp_server') {
77
+ const named = MCP_SERVER_ACTOR_RE.exec(question.actor);
78
+ if (named) return named[1];
79
+ }
80
+ const suffix = `.${question.tool}`;
81
+ for (let i = rows.length - 1; i >= 0; i--) {
82
+ const echo = parseCallingEcho(rows[i]);
83
+ if (!echo) continue;
84
+ if (echo.qualified.length > suffix.length && echo.qualified.endsWith(suffix)) {
85
+ return echo.qualified.slice(0, -suffix.length);
86
+ }
87
+ }
88
+ return CODEX_APPS_SERVER;
89
+ }
17
90
 
18
91
  // Scan bottom-up (the live prompt is at the end of the viewport). Prefer the
19
- // explicit "Allow the … server to run tool …?" question; fall back to the
20
- // "Calling server.tool(" echo Codex prints just above the approval widget.
92
+ // explicit question — its quoted tool name is authoritative — and fall back to
93
+ // the "Calling <qualified>" echo Codex prints just above the approval widget.
94
+ //
95
+ // Returns { server, tool, questionIdx, actorKind } | null. `actorKind` is null
96
+ // on the echo-only path (there is no question actor to classify).
21
97
  function extractMcpServerTool(lines) {
22
- for (let i = lines.length - 1; i >= 0; i--) {
23
- const q = lines[i].match(QUESTION_RE);
24
- if (q) return { server: q[1], tool: q[2], questionIdx: i };
98
+ const rows = Array.isArray(lines) ? lines : [];
99
+
100
+ for (let i = rows.length - 1; i >= 0; i--) {
101
+ const question = parseApprovalQuestion(rows[i]);
102
+ if (!question) continue;
103
+ return {
104
+ server: serverForQuestion(question, rows),
105
+ tool: question.tool,
106
+ questionIdx: i,
107
+ actorKind: question.actorKind,
108
+ };
25
109
  }
26
- for (let i = lines.length - 1; i >= 0; i--) {
27
- const c = lines[i].match(CALLING_RE);
28
- if (c) return { server: c[1], tool: c[2], questionIdx: -1 };
110
+
111
+ for (let i = rows.length - 1; i >= 0; i--) {
112
+ const echo = parseCallingEcho(rows[i]);
113
+ if (!echo) continue;
114
+ // Split at the FIRST dot: 'codex_apps.google_drive.update_file' is server
115
+ // 'codex_apps' + tool 'google_drive.update_file' (a greedy split at the last
116
+ // dot silently truncated the tool to 'update_file').
117
+ const dot = echo.qualified.indexOf('.');
118
+ if (dot <= 0) continue;
119
+ return {
120
+ server: echo.qualified.slice(0, dot),
121
+ tool: echo.qualified.slice(dot + 1),
122
+ questionIdx: -1,
123
+ actorKind: null,
124
+ };
29
125
  }
126
+
30
127
  return null;
31
128
  }
32
129
 
33
- module.exports = { CALLING_RE, QUESTION_RE, extractMcpServerTool };
130
+ module.exports = {
131
+ CODEX_APPS_SERVER,
132
+ parseApprovalQuestion,
133
+ parseCallingEcho,
134
+ extractMcpServerTool,
135
+ };
@@ -10,8 +10,17 @@
10
10
  // 3. Always allow
11
11
  // 4. Cancel
12
12
  // enter to submit | esc to cancel
13
-
14
- const { QUESTION_RE, CALLING_RE, extractMcpServerTool } = require('./codex-mcp-extract');
13
+ //
14
+ // The question's actor is structural, not a fixed list: "the <server> MCP
15
+ // server" (ordinary MCP server), "this app" (server `codex_apps`, connector
16
+ // unnamed) or the connector's display name (e.g. "Google Drive", also server
17
+ // `codex_apps`). The echo line may carry no "({...})" argument list.
18
+
19
+ const {
20
+ parseApprovalQuestion,
21
+ parseCallingEcho,
22
+ extractMcpServerTool,
23
+ } = require('./codex-mcp-extract');
15
24
  const ALLOW_RE = /^\s*[❯›> ]?\s*1\.\s*Allow\b/i;
16
25
  // At least one of the "remember this" options — the structural twin of "1. Allow"
17
26
  // that distinguishes a live MCP approval menu from a stray "Allow" in prose.
@@ -47,13 +56,13 @@ module.exports = {
47
56
  const lines = String(text || '').split('\n').map(l => l.trim()).filter(Boolean);
48
57
  if (lines.length < 3) return false;
49
58
 
50
- // The MCP question ("Allow the <server> MCP server to run tool …?") or the
51
- // "Calling server.tool(" echo is the strongest, most stable signal. Codex's
59
+ // The MCP question ("Allow <actor> to run tool …?") or the
60
+ // "Calling server.tool" echo is the strongest, most stable signal. Codex's
52
61
  // footer hint ("enter to submit | esc to cancel") was DROPPED in newer builds
53
62
  // (0.142.x) — anchoring detection on that cosmetic string silently broke every
54
63
  // MCP auto-approval when the UI changed, so it is no longer used at all. Detect
55
64
  // on the invariant structure instead.
56
- const hasQuestion = lines.some(l => QUESTION_RE.test(l)) || lines.some(l => CALLING_RE.test(l));
65
+ const hasQuestion = lines.some(l => parseApprovalQuestion(l) || parseCallingEcho(l));
57
66
  if (!hasQuestion) return false;
58
67
 
59
68
  // Require a LIVE Allow-option block near the viewport bottom: "1. Allow" plus at