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
@@ -13,13 +13,12 @@ const {
13
13
  portkeyKeyFingerprint,
14
14
  } = require('./portkey');
15
15
  const { scaleDecimal, canonicalRateNumber } = require('../../shared/usage-pricing');
16
- const { redactSecrets } = require('./provider-error');
17
16
  const { findSupportedModel } = require('./supported-models');
18
17
  const { isDecideOnlyModel } = require('./decide-only');
19
18
  const { generateRouteGroups } = require('./route-groups');
20
19
  const { scheduleRebuild } = require('./model-identity');
21
20
  const { notifyModelRegistryChanged } = require('../lib/ctm-model-registry-notify');
22
- const { createRetryPolicy } = require('../lib/kernel/retry-policy');
21
+ const { fetchJsonOrError, safeJson, _setRetrySleepForTest } = require('./portkey-http');
23
22
 
24
23
  const PORTKEY_SYNC_INTERVAL_MS = 60 * 60 * 1000;
25
24
  const PORTKEY_SYNC_INITIAL_DELAY_MS = 15 * 1000;
@@ -28,10 +27,6 @@ const PORTKEY_CONTROL_PLANE_URL = 'https://api.portkey.ai/v1';
28
27
  const PORTKEY_PRICING_BASE_URL = 'https://configs.portkey.ai/pricing';
29
28
  const PORTKEY_DISCOVERY_PAGE_SIZE = 50;
30
29
  const PORTKEY_CATALOG_PAGE_SIZE = 500;
31
- // D-2: TOTAL attempts for a transient failure, with the backoff between them.
32
- const PORTKEY_REQUEST_ATTEMPTS = 3;
33
- const PORTKEY_RETRY_DELAYS_MS = [1000, 3000];
34
- const PORTKEY_RETRY_AFTER_MAX_MS = 30 * 1000;
35
30
 
36
31
  // D-9: vendor names Wall-E has a dedicated LLM adapter for. A discovered
37
32
  // provider whose vendor slug does not resolve to one of these keeps its own
@@ -96,141 +91,6 @@ function inferGatewayCapabilities(providerType, model = {}) {
96
91
  return supported?.capabilities || (isDecideOnlyModel({ providerType, modelId: id }) ? ['decide'] : ['chat']);
97
92
  }
98
93
 
99
- // WP-1 review R6: this is the ONLY request budget in the Portkey path, and it
100
- // is per REQUEST, not per host — callers that must bound a whole host (the
101
- // resolver) also cap the number of pages. There is deliberately no
102
- // AbortController+setTimeout fallback when `AbortSignal.timeout` is missing:
103
- // that would be a recovery timer, which `stabilization-lint` requires to be
104
- // registered in release/ownership-registry.json and which this feature's
105
- // design forbids. `AbortSignal.timeout` is Node >= 17.3 and this repo pins
106
- // v25.2.1, so the fallback branch below is unreachable in practice; if it is
107
- // ever reached the request is unbounded and the caller's page cap is what
108
- // stops it.
109
- function withTimeout(timeoutMs) {
110
- if (typeof AbortSignal !== 'undefined' && AbortSignal.timeout && Number.isFinite(timeoutMs)) {
111
- return { signal: AbortSignal.timeout(timeoutMs) };
112
- }
113
- return {};
114
- }
115
-
116
- async function safeJson(response) {
117
- try { return await response.json(); } catch { return null; /* expected: non-JSON or empty error body; callers fall back to a generic message */ }
118
- }
119
-
120
- // D-2 test seam: when set, createRetryPolicy uses it instead of the kernel's
121
- // own registered timer. null (default) means "use the kernel sleep".
122
- let _retrySleepOverride = null;
123
-
124
- function _setRetrySleepForTest(fn) {
125
- _retrySleepOverride = typeof fn === 'function' ? fn : null;
126
- }
127
-
128
- // D-1: TRANSIENT iff no response at all (status 0) or 408/429/5xx; every other
129
- // status (3xx/401/403/AB03/033/other 4xx) is DEFINITIVE and never retried.
130
- function isTransientStatus(status) {
131
- if (status === 0) return true;
132
- const code = Number(status);
133
- return code === 408 || code === 429 || (code >= 500 && code <= 599);
134
- }
135
-
136
- // D-2: honour Retry-After only as integer seconds ≤ 30 s; else fixed backoff.
137
- function retryAfterMs(response) {
138
- if (!response || typeof response.headers?.get !== 'function') return null;
139
- const raw = response.headers.get('retry-after');
140
- if (raw == null) return null;
141
- const seconds = Number(String(raw).trim());
142
- if (!Number.isFinite(seconds) || seconds < 0) return null;
143
- const ms = seconds * 1000;
144
- return ms <= PORTKEY_RETRY_AFTER_MAX_MS ? ms : null;
145
- }
146
-
147
- function errorCodeFromBody(body) {
148
- if (!body || typeof body !== 'object') return '';
149
- if (body.data && body.data.errorCode) return String(body.data.errorCode);
150
- if (body.error && body.error.code) return String(body.error.code);
151
- return '';
152
- }
153
-
154
- function errorMessageFromBody(body, fallback) {
155
- if (body && typeof body === 'object') {
156
- if (body.data && body.data.message) return String(body.data.message);
157
- if (body.message) return String(body.message);
158
- }
159
- return fallback;
160
- }
161
-
162
- // `redirect: 'manual'` is the DEFAULT here, for EVERY request in the Portkey
163
- // path — not only the resolver's probe (final review, finding #3). Every one
164
- // carries the full gateway key in `x-portkey-api-key`, and undici keeps that
165
- // header across a cross-origin redirect, so following a 3xx would hand the key
166
- // to a host the user never named (the axios GHSA-jr5f-v2jv-69x6 class). With
167
- // `manual` a 3xx is not followed and `response.ok` is false, so it surfaces as
168
- // `{ok:false, status:3xx}` — a verdict, never a second request.
169
- // `fetchInit` (WP-1): extra init the CALLER owns — today the resolver's
170
- // per-host AbortController signal. Spread last so it replaces withTimeout's.
171
- // D-2: ONE attempt; returns today's ok/failure shape plus the response's
172
- // Retry-After (null when the fetch threw — a transport failure has no headers).
173
- async function fetchJsonOnce(fetchFn, url, { headers, timeoutMs, fallbackMessage, fetchInit } = {}) {
174
- try {
175
- const response = await fetchFn(url, { headers, redirect: 'manual', ...withTimeout(timeoutMs), ...(fetchInit || {}) });
176
- const body = await safeJson(response);
177
- if (response.ok) return { result: { ok: true, status: response.status, body }, retryAfter: null };
178
- return {
179
- result: {
180
- ok: false,
181
- status: response.status,
182
- code: errorCodeFromBody(body),
183
- message: redactSecrets(errorMessageFromBody(body, fallbackMessage(response.status))),
184
- },
185
- retryAfter: retryAfterMs(response),
186
- };
187
- } catch (err) {
188
- // D-1: no HTTP response at all (status 0 — transport/timeout) is transient.
189
- const message = err && err.message ? err.message : String(err);
190
- return { result: { ok: false, status: 0, code: '', message: redactSecrets(message) }, retryAfter: null };
191
- }
192
- }
193
-
194
- // D-2: transient failures are retried through wall-e's ONE retry engine
195
- // (lib/kernel/retry-policy.js); a caller owning its own abort signal (the
196
- // resolver's single probe) is single-shot and never retries.
197
- async function fetchJsonOrError(fetchFn, url, opts = {}) {
198
- if (opts.fetchInit) {
199
- const { result } = await fetchJsonOnce(fetchFn, url, opts);
200
- if (result.ok) return result;
201
- const transient = isTransientStatus(result.status);
202
- return transient ? { ...result, transient, attempts: 1 } : { ...result, transient };
203
- }
204
- let nextDelay = null;
205
- let lastAttempt = 1;
206
- const policyOptions = {
207
- maxAttempts: PORTKEY_REQUEST_ATTEMPTS,
208
- jitter: 0,
209
- delayFn: (attempt) => nextDelay ?? PORTKEY_RETRY_DELAYS_MS[attempt - 1],
210
- classify: (err) => (err && err.portkeyTransient ? 'transient' : 'permanent'),
211
- layer: 'portkey-sync',
212
- operation: 'fetch',
213
- };
214
- // Only override the kernel's own registered timer when a test seam is set.
215
- if (_retrySleepOverride) policyOptions.sleep = (ms) => _retrySleepOverride(ms);
216
- const policy = createRetryPolicy(policyOptions);
217
- try {
218
- return await policy.run(async (attempt) => {
219
- const { result, retryAfter } = await fetchJsonOnce(fetchFn, url, opts);
220
- if (result.ok || !isTransientStatus(result.status)) {
221
- return result.ok ? result : { ...result, transient: false };
222
- }
223
- nextDelay = retryAfter;
224
- lastAttempt = attempt;
225
- throw Object.assign(new Error(result.message), { portkeyTransient: true, result });
226
- });
227
- } catch (err) {
228
- // Only our transient sentinel is converted; anything else propagates.
229
- if (!err || !err.portkeyTransient) throw err;
230
- return { ...err.result, transient: true, attempts: lastAttempt };
231
- }
232
- }
233
-
234
94
  // D-3: per-integration data-plane listing, scoped by the '@slug' header —
235
95
  // never a virtual key.
236
96
  async function listPortkeyModelsForIntegration({ baseUrl, apiKey, slug, fetchFn = fetch, timeoutMs = 8000 }) {
@@ -308,6 +168,7 @@ async function listPortkeyCatalogModels({
308
168
  let total = Infinity;
309
169
  let seen = 0;
310
170
  let pages = 0;
171
+ let last = {}; // WP-0: last page's HTTP status + raw body.total ride along as evidence for catalog_empty
311
172
  while (seen < total && pages < maxPages) {
312
173
  const url = `${root}/models?offset=${offset}&limit=${pageSize}`;
313
174
  const result = await fetchJsonOrError(fetchFn, url, {
@@ -319,6 +180,7 @@ async function listPortkeyCatalogModels({
319
180
  if (!result.ok) return result;
320
181
  const data = Array.isArray(result.body?.data) ? result.body.data : [];
321
182
  total = Number.isFinite(result.body?.total) ? result.body.total : data.length;
183
+ last = { status: typeof result.status === 'number' ? result.status : null, total: Number.isFinite(result.body?.total) ? result.body.total : null };
322
184
  for (const model of data) {
323
185
  const id = String(model.id || '').trim();
324
186
  const match = id.match(/^@([^/]+)\/(.+)$/);
@@ -332,7 +194,7 @@ async function listPortkeyCatalogModels({
332
194
  if (!data.length) break;
333
195
  offset += pageSize;
334
196
  }
335
- return { ok: true, bySlug };
197
+ return { ok: true, bySlug, status: last.status ?? null, total: last.total ?? null };
336
198
  }
337
199
 
338
200
  function extractPortkeyPricing(modelConfig = {}) {
@@ -474,9 +336,10 @@ function upsertIntegrationRow(brain, gateway, provider, rows, now) {
474
336
  return { rowId, slug, type: vendorType, enabled };
475
337
  }
476
338
 
477
- // D-4: an `ok` listing (catalog or passthrough, empty or not) is authoritative —
478
- // it refreshes rows and disables omitted ones ('manual' rows excepted). A
479
- // transient failure never reaches here (it is reported, D-3).
339
+ // D-4/D-6: a NON-EMPTY `ok` listing (catalog or passthrough) is authoritative —
340
+ // it refreshes rows and disables omitted ones ('manual' rows excepted). An empty
341
+ // `ok` listing for an integration with ≥1 enabled row never reaches here: it is
342
+ // reported as `catalog_empty` (D-6, 2026-10-22 incident). Transient failures too (D-3).
480
343
  function upsertLiveModels(brain, rowInfo, models, verificationStatus, now) {
481
344
  const seenIds = new Set();
482
345
  let imported = 0;
@@ -520,6 +383,13 @@ async function syncModelsForIntegration(brain, gateway, rowInfo, catalog, { fetc
520
383
 
521
384
  if (catalog.ok) {
522
385
  models = catalog.bySlug.get(rowInfo.slug) || [];
386
+ // D-6 (WP-0): a zero-model ok listing never disables rows enabled now (the control plane timed
387
+ // out while the catalog still answered 200, 2026-10-22) — report it via the error branch below.
388
+ const enabledNow = models.length ? 0 : (brain.listModelsByProvider?.(rowInfo.rowId) || []).length;
389
+ if (enabledNow > 0) {
390
+ console.warn(`[portkey-sync] catalog_empty slug=${rowInfo.slug} row=${rowInfo.rowId} enabled=${enabledNow} status=${catalog.status ?? 200} total=${catalog.total ?? 'n/a'} data=0`);
391
+ listError = { ok: false, status: catalog.status ?? 200, code: 'catalog_empty', message: `Portkey catalog listed 0 models for ${rowInfo.slug} while ${enabledNow} are enabled; rows kept` };
392
+ }
523
393
  } else if (catalog.transient) {
524
394
  // D-3: a transient catalog failure says nothing about this integration's
525
395
  // models — report and change NOTHING (never swap in a passthrough listing;
@@ -268,6 +268,63 @@ function callerDoorBearerOk(req) {
268
268
  return require('../http/auth').requireBearer(req, strFlag('WALL_E_API_TOKEN', ''));
269
269
  }
270
270
 
271
+ // WP-5b (lead-cost routing §6.2/§6.7): the rule cell decides whether a wait parks the watcher.
272
+ // 'off' is a user veto (D-18: final, and silent ticks cannot move it), 'silent' is an off cell
273
+ // still collecting 1-in-10 ticks (D-16), and CTM's default 'on' is returned as null so an
274
+ // unchanged request keeps its exact shape (`watch=` is omitted entirely). A route wait starts
275
+ // no watcher (WP-1), and a wait whose worker has no recorded route has no cell to read.
276
+ const WATCH_ORDER = Object.freeze({ off: 0, silent: 1, on: 2 });
277
+ const WATCH_OFF_CELL = 'off';
278
+
279
+ function _helperRulesDb() {
280
+ try {
281
+ // lib/brain-events is the one legal way to the db handle from llm/ (dependency-rules.json).
282
+ return require('../lib/brain-events').getDb();
283
+ } catch {
284
+ // expected: the brain is not initialized (a bare unit call) -- a missing handle reads 'on'
285
+ return null;
286
+ }
287
+ }
288
+
289
+ function _watchModeOfRoute(route, db) {
290
+ const { readCell, readCellState } = require('../lib/helper-rules/store');
291
+ const cellKey = `wait-watch|default|${route}`;
292
+ const row = readCell(db, cellKey);
293
+ if (row && Number(row.user_veto) === 1) return 'off';
294
+ return readCellState(db, cellKey) === WATCH_OFF_CELL ? 'silent' : 'on';
295
+ }
296
+
297
+ /**
298
+ * The `watch` mode one wait request carries, or null to omit the parameter (CTM's default
299
+ * 'on'). `hook` is lib/helper-rules/lead-call-hook.js and `db` the brain handle — both
300
+ * injectable so a test can drive the cell state directly.
301
+ */
302
+ function watchModeFor(args = {}, hook = require('../lib/helper-rules/lead-call-hook'), db = _helperRulesDb()) {
303
+ const routes = new Set();
304
+ if (args.route) return null; // a route wait starts no watcher (WP-1)
305
+ try {
306
+ if (args.ctm_session_id) {
307
+ const entry = hook.workerRouteOf(args.ctm_session_id);
308
+ if (!entry || !entry.route) return null; // unknown -> 'on'
309
+ routes.add(entry.route);
310
+ } else {
311
+ for (const worker of hook.leadWorkersOf(args.parent_agent_session_id) || []) {
312
+ if (worker && worker.route) routes.add(worker.route);
313
+ }
314
+ if (!routes.size) return null; // nothing recorded -> 'on'
315
+ }
316
+ } catch {
317
+ // expected: the hook cannot answer -- 'on' is the fail-open default, never a failed wait
318
+ return null;
319
+ }
320
+ let mode = 'on';
321
+ for (const route of routes) {
322
+ const next = _watchModeOfRoute(route, db);
323
+ if (WATCH_ORDER[next] < WATCH_ORDER[mode]) mode = next; // the most restrictive route wins
324
+ }
325
+ return mode === 'on' ? null : mode;
326
+ }
327
+
271
328
  // WP-5 (lead-aware wait, D-12, §6.5 E): one place decides which CTM call a worker
272
329
  // status/wait tool makes and caps any report text it carries. It lives here, outside
273
330
  // mcp-server.js's frozen ceiling: a named worker reads/waits on that worker alone with the
@@ -281,15 +338,19 @@ async function resolveWorkerWaitResult(ctmContextClient, toolName, args, reportT
281
338
  if (isWait && args.route && args.ctm_session_id) {
282
339
  return { error: 'wait_args_conflict', detail: 'ctm_session_id and route are mutually exclusive' };
283
340
  }
341
+ // WP-5b: the watcher mode rides the WAIT request only (a plain status read starts none).
342
+ const watch = isWait ? watchModeFor(args) : null;
284
343
  if (args.ctm_session_id) {
285
344
  result = await ctmContextClient.workerStatus(args.ctm_session_id, {
286
345
  timeoutMs: args.timeout_ms || 30000, includeReport: true, waitMs,
287
346
  leadAgentSessionId: isWait ? args.parent_agent_session_id : undefined,
347
+ ...(watch ? { watch } : {}),
288
348
  });
289
349
  } else if (isWait) {
290
350
  result = await ctmContextClient.waitForWorkers({
291
351
  leadAgentSessionId: args.parent_agent_session_id, waitMs, timeoutMs: args.timeout_ms || 30000,
292
352
  ...(args.route ? { route: String(args.route) } : {}),
353
+ ...(watch ? { watch } : {}),
293
354
  });
294
355
  } else {
295
356
  result = await ctmContextClient.workerStatus(args.ctm_session_id, { timeoutMs: args.timeout_ms || 30000, includeReport: true });
@@ -12,7 +12,7 @@
12
12
  // gateway can sit behind any hostname).
13
13
 
14
14
  const { parseCustomHeaders, isPortkeyRouteRow, portkeyRequestHeaders } = require('./portkey');
15
- const { providerReasoningParams } = require('../../shared/reasoning-control');
15
+ const { providerReasoningParams, resolveReasoningControl } = require('../../shared/reasoning-control');
16
16
  const { validateModelDeployments, canonicalJson } = require('../../shared/coding-model-deployment');
17
17
  const crypto = require('node:crypto');
18
18
 
@@ -165,6 +165,45 @@ function routeReasoningLevelsForRow(row) {
165
165
  return routeReasoningLevels(reasoningParamsForRoute(normalizeRowType(row), upstreamApisForRow(row).upstream_apis));
166
166
  }
167
167
 
168
+ /** The Claude Code --effort vocabulary (code.claude.com/docs/en/model-config). Frozen; display order. */
169
+ const CLAUDE_CLI_EFFORT_LEVELS = Object.freeze(['low', 'medium', 'high', 'xhigh', 'max']);
170
+
171
+ /**
172
+ * D-1: the CLI effort levels a (route, wire model) pair can carry.
173
+ * - upstream_apis includes 'anthropic-messages' -> CLAUDE_CLI_EFFORT_LEVELS
174
+ * filtered to the Claude matrix for `model_id`
175
+ * (resolveReasoningControl({ providerType: 'anthropic', modelId }).values;
176
+ * a 'fixed' control, or an unparseable id, yields []).
177
+ * A passthrough anthropic-messages route relays the Claude CLI's own
178
+ * `output_config.effort` verbatim, so the CLI vocabulary -- not the
179
+ * chat-translate fragment map, which is empty for this wire shape -- is
180
+ * the truth here.
181
+ * - otherwise -> routeReasoningLevels(reasoningParamsForRoute(type,
182
+ * upstream_apis)) unchanged (D-4): the chat-translate fragment producer
183
+ * still decides for every other upstream API.
184
+ * Always returns a NEW array; never null; never throws on garbage input.
185
+ * @param {{ type?: string, upstream_apis?: string[], model_id?: string }} [args]
186
+ * @returns {string[]}
187
+ */
188
+ function routeModelReasoningLevels(args) {
189
+ // Tolerate a null/undefined/garbage argument container: callers project rows
190
+ // from the brain and a malformed row must read as "no levels", never throw.
191
+ const { type = '', upstream_apis = [], model_id = '' } = (args && typeof args === 'object' && !Array.isArray(args)) ? args : {};
192
+ const apis = Array.isArray(upstream_apis) ? upstream_apis : [];
193
+ if (apis.indexOf(ANTHROPIC_MESSAGES_API) !== -1) {
194
+ const control = resolveReasoningControl({ providerType: 'anthropic', modelId: model_id });
195
+ const allowed = new Set((Array.isArray(control?.values) ? control.values : []).map((option) => option?.value));
196
+ return CLAUDE_CLI_EFFORT_LEVELS.filter((level) => allowed.has(level));
197
+ }
198
+ return routeReasoningLevels(reasoningParamsForRoute(String(type || '').toLowerCase(), apis));
199
+ }
200
+
201
+ /** Row convenience, mirrors routeReasoningLevelsForRow. */
202
+ function routeModelReasoningLevelsForRow(row, modelId) {
203
+ const { upstream_apis } = upstreamApisForRow(row);
204
+ return routeModelReasoningLevels({ type: normalizeRowType(row), upstream_apis, model_id: modelId });
205
+ }
206
+
168
207
  function publicWorkerCapability(row) {
169
208
  const capabilities = row.capabilities || JSON.parse(row.capabilities_json || '{}');
170
209
  const expired = row.state === 'unknown';
@@ -312,4 +351,7 @@ module.exports = {
312
351
  reasoningParamsForRoute,
313
352
  routeReasoningLevels,
314
353
  routeReasoningLevelsForRow,
354
+ CLAUDE_CLI_EFFORT_LEVELS,
355
+ routeModelReasoningLevels,
356
+ routeModelReasoningLevelsForRow,
315
357
  };
@@ -594,6 +594,44 @@ function finishConversationBootstrap({ state, batchLimit = 500 } = {}) {
594
594
  }).immediate();
595
595
  }
596
596
 
597
+ // Lead-cost routing WP-5a (design §6.6/§6.8, contract §2): the helper-rules record's four
598
+ // writes. lib/helper-rules/store.js owns the SQL and takes the handle; these wrappers are
599
+ // what brainWrite resolves — same function on the direct (RW) path and on the owner-routed
600
+ // path (workers/brain-owner-worker.js's CUSTOM_BRAIN_CALLS). WP-6 adds the fifth
601
+ // (helperRulesSetGrade): the spot-check grader's verdict on one explore row; round 2 WP-2 adds
602
+ // the two below it (D-5's close-by-worker and D-5/D-6's relabel).
603
+ function helperRulesRecordOutcome(outcome) {
604
+ return require('../lib/helper-rules/store').recordOutcome(brain.getDb(), outcome);
605
+ }
606
+
607
+ function helperRulesCloseOutcome(id, patch) {
608
+ return require('../lib/helper-rules/store').closeOutcome(brain.getDb(), id, patch);
609
+ }
610
+
611
+ // Round 2 WP-2 (§6.4 S-16): D-5's close path is keyed on the row's worker_id, so a Wall-E
612
+ // restart, an evicted in-memory map or a second process cannot orphan an open row.
613
+ function helperRulesCloseOutcomeByWorker(workerId, patch) {
614
+ return require('../lib/helper-rules/store').closeOutcomeByWorker(brain.getDb(), workerId, patch);
615
+ }
616
+
617
+ // Round 2 WP-2 (§6.4 S-16): the one-time relabel + D-6 recovery pass (idempotent — the second
618
+ // run changes nothing). The daily tick calls it too.
619
+ function helperRulesRelabel(options) {
620
+ return require('../lib/helper-rules/store').relabel(brain.getDb(), options);
621
+ }
622
+
623
+ function helperRulesSetCellState(cellKey, options) {
624
+ return require('../lib/helper-rules/store').setCellState(brain.getDb(), cellKey, options);
625
+ }
626
+
627
+ function helperRulesDailyTick() {
628
+ return require('../lib/helper-rules/store').dailyTick(brain.getDb());
629
+ }
630
+
631
+ function helperRulesSetGrade(id, gradeJson) {
632
+ return require('../lib/helper-rules/store').setGrade(brain.getDb(), id, gradeJson);
633
+ }
634
+
597
635
  module.exports = {
598
636
  failConversationExtraction,
599
637
  markMemoriesExtractionSkipped,
@@ -620,4 +658,11 @@ module.exports = {
620
658
  ingestUpdateMemoryContent,
621
659
  runDbOptimizeMigration,
622
660
  recordProviderHealthStatus,
661
+ helperRulesRecordOutcome,
662
+ helperRulesCloseOutcome,
663
+ helperRulesSetCellState,
664
+ helperRulesDailyTick,
665
+ helperRulesSetGrade,
666
+ helperRulesCloseOutcomeByWorker,
667
+ helperRulesRelabel,
623
668
  };
@@ -28,12 +28,47 @@ const SERVER_INSTRUCTIONS = [
28
28
  '`worker_started` already launched a separate worker: wait on it and review its report -- a process start is never task completion.',
29
29
  "Keep the lead's own model and account.",
30
30
  'Read-only census and review work can be delegated the same way with `worker_profile: "review"`: the worker cannot edit files, and you keep the verdict.',
31
+ 'Wait on workers with walle_worker_wait and omit wait_ms; it wakes on a finished, failed, resumed or stuck worker, on a question, and when your user types -- never sleep-and-check or wrap it in a short-yield script.',
31
32
  '',
32
33
  'This guidance is advice, not enforcement: it never authorizes spawning extra agents on your own initiative, and Wall-E cannot intercept or reroute native `spawn_agent` calls you make without it.',
33
34
  '',
34
35
  "Use Wall-E memory before public web search or guesswork for private, remembered, or work-context questions: prior sessions, decisions, preferences, people, projects, documents, and the user's own style. `walle_context_hint` gives cheap preflight routing, `walle_lookup_context` one-shot private context, `walle_route_query` a routing verdict when unsure, and `walle_memory_status` connection health. In Codex, prefer `CODEX_THREAD_ID` over `CTM_SESSION_ID` for current-session lookups.",
35
36
  ].join('\n');
36
37
 
38
+ // WP-4 (lead-cost-routing-round1-2026-10-21.html §6.4 "Lead: exploration hand-off"): the block a
39
+ // lead harness receives ONLY while its `explore` rule cell is 'on' or 'probe' (lib/helper-rules/
40
+ // cell-state.js reads it; WP-5 owns the cell's storage and the switch). It is appended to
41
+ // SERVER_INSTRUCTIONS, never merged into it, so SERVER_INSTRUCTIONS stays the stable text every
42
+ // brain serves and the first 512 characters never move.
43
+ //
44
+ // Like the routing guidance above, this is ADVICE: the lead keeps the verdict, a review worker
45
+ // cannot edit files, and nothing here authorizes delegation the user did not ask for.
46
+ const EXPLORE_GUIDANCE = [
47
+ '## Lead: exploration hand-off',
48
+ '- If the question you are answering will take more than two reads and is not the exact lines you are about to edit,',
49
+ ' hand it off before the third read. Write the brief FIRST, then call',
50
+ ' walle_delegate_implementation({ brief_path, cwd, agent, worker_profile: "review" }) with no model or route.',
51
+ ' If the result is resolved_native, run your own read-only sub-agent with the same brief instead.',
52
+ ' Brief (four fields, nothing else):',
53
+ ' Question: <one sentence>',
54
+ ' Known so far: <facts you already have, with file:line>',
55
+ ' Start from: <paths you already know matter> (optional)',
56
+ ' Stop when: <the condition that answers the question>',
57
+ ' Expected report (<= 600 words): a table of file:line + a <=5-line quote per row; INFERENCE marks; a "not found" row is valid.',
58
+ '- Check the report before using it: at least one file:line row and the Stop-when condition addressed. If not, or the',
59
+ ' worker failed, read it yourself once. Never hand the same question off twice.',
60
+ "- Trust the report's file:line facts; re-read only the lines you will change.",
61
+ ].join('\n');
62
+
63
+ // The one builder both transports call with the state their reader resolved; 'off' (and anything
64
+ // unrecognized) serves SERVER_INSTRUCTIONS byte-for-byte, so the guidance is switchable per lead
65
+ // harness without a restart and without a second copy of the text.
66
+ function buildServerInstructions({ explore = 'on' } = {}) {
67
+ return (explore === 'on' || explore === 'probe')
68
+ ? `${SERVER_INSTRUCTIONS}\n\n${EXPLORE_GUIDANCE}`
69
+ : SERVER_INSTRUCTIONS;
70
+ }
71
+
37
72
  // WP-2 correction (lead review): the managed agent-instruction block is the SECOND surface of the
38
73
  // same shared guidance, so its owner lives here too -- one pure builder owning the full text, with
39
74
  // no I/O, no filesystem and no dependencies (the two host-derived values arrive as arguments).
@@ -123,7 +158,10 @@ function handleProtocolMessage(message, options = {}) {
123
158
  serverInfo: SERVER_INFO,
124
159
  // WP-2: shared guidance every transport serves verbatim (MCP initialize result
125
160
  // instructions) -- never a per-transport variant, so stdio and HTTP cannot drift.
126
- instructions: SERVER_INSTRUCTIONS,
161
+ // WP-4: the lead harness's exploration hand-off block rides on top ONLY when the
162
+ // transport resolved its `explore` cell (options.leadGuidance); with no option the
163
+ // reply is SERVER_INSTRUCTIONS, exactly as before this rule existed.
164
+ instructions: options.leadGuidance ? buildServerInstructions(options.leadGuidance) : SERVER_INSTRUCTIONS,
127
165
  }),
128
166
  },
129
167
  };
@@ -142,11 +180,13 @@ function handleProtocolMessage(message, options = {}) {
142
180
 
143
181
  module.exports = {
144
182
  CAPABILITIES,
183
+ EXPLORE_GUIDANCE,
145
184
  PROTOCOL_VERSION,
146
185
  SERVER_INFO,
147
186
  SERVER_INSTRUCTIONS,
148
187
  SUPPORTED_PROTOCOL_VERSIONS,
149
188
  buildManagedAgentInstructions,
189
+ buildServerInstructions,
150
190
  handleProtocolMessage,
151
191
  mcpError,
152
192
  mcpResponse,
@@ -3,6 +3,8 @@ const fs = require('node:fs');
3
3
  const path = require('node:path');
4
4
  const { randomUUID: uuidv4 } = require('node:crypto');
5
5
  const brain = require('./brain');
6
+ const { leadGuidanceForMessage } = require('./lib/helper-rules/cell-state');
7
+ const { recordLeadToolCall } = require('./lib/helper-rules/lead-call-hook'); // WP-5b §6.7: one-line lead-tool hook (S-11/S-13)
6
8
  const { brainWrite } = require('./loops/brain-write');
7
9
  const { normalizeKnowledgeCategory } = require('./brain/knowledge-categories');
8
10
  const ctmSessionContext = require('./memory/ctm-session-context');
@@ -848,15 +850,22 @@ async function executeTool(name, args = {}, context = {}) {
848
850
  // high-confidence or non-identity match, matching the test plan's
849
851
  // "delegate with a 1.0 row never calls it" case.
850
852
  if (typeof identityVerified === 'boolean') result.identity_verified = identityVerified;
853
+ recordLeadToolCall({ agentSessionId: args.parent_agent_session_id, tool: name, args, result, at: Date.now() });
851
854
  return result;
852
855
  }
853
856
  case 'walle_worker_status': case 'walle_worker_wait': {
854
857
  // WP-5 (D-12): the branching (named worker vs whole-set wait vs the no-identity
855
858
  // 400 result) and the report-text cap live in worker-mcp-routing.js's
856
859
  // resolveWorkerWaitResult -- mcp-server.js is at its frozen line ceiling.
857
- return resolveWorkerWaitResult(ctmContextClient, name, args, WORKER_STATUS_REPORT_TEXT_CAP_BYTES);
860
+ const result = await resolveWorkerWaitResult(ctmContextClient, name, args, WORKER_STATUS_REPORT_TEXT_CAP_BYTES);
861
+ recordLeadToolCall({ agentSessionId: args.parent_agent_session_id, tool: name, args, result, at: Date.now() });
862
+ return result;
863
+ }
864
+ case 'walle_stop_worker': {
865
+ const result = await resolveWorkerStopResult(ctmContextClient, args);
866
+ recordLeadToolCall({ agentSessionId: args.parent_agent_session_id, tool: name, args, result, at: Date.now() });
867
+ return result;
858
868
  }
859
- case 'walle_stop_worker': return resolveWorkerStopResult(ctmContextClient, args);
860
869
  case 'walle_resume_worker': {
861
870
  const result = await ctmContextClient.resumeWorker(args.ctm_session_id, args.message, {
862
871
  timeoutMs: args.timeout_ms || 30000, clientMessageId: args.client_message_id,
@@ -866,6 +875,7 @@ async function executeTool(name, args = {}, context = {}) {
866
875
  // (worker-continuation.js) -- undefined leaves the key out, as parentAgentSessionId does.
867
876
  callerToolUseId: args.caller_tool_use_id,
868
877
  });
878
+ recordLeadToolCall({ agentSessionId: args.parent_agent_session_id, tool: name, args, result, at: Date.now() });
869
879
  return projectWorkerResumeResult(result, { ctmSessionId: args.ctm_session_id });
870
880
  }
871
881
  case 'walle_list_sources': {
@@ -2734,6 +2744,7 @@ async function handleMcpMessage(msg, context = {}) {
2734
2744
  // that do not need a response. Stdio keeps this false and proxies future
2735
2745
  // notification methods here instead of silently swallowing them.
2736
2746
  acceptAnyNotification: true,
2747
+ leadGuidance: leadGuidanceForMessage(msg, brain), // WP-4 §6.4: gates the exploration hand-off block
2737
2748
  });
2738
2749
  if (control.handled) return control.result;
2739
2750
 
@@ -342,6 +342,15 @@ const WORKER_STATUS_OUTPUT_SCHEMA = {
342
342
  // §6.4): which source report_text came from: report, last_message, or a successful
343
343
  // final provider result (status stays exited and report_present stays false).
344
344
  report_source: { type: ['string', 'null'], enum: ['report', 'last_message', 'final_result', null] },
345
+ // lead-cost routing round 2 D-3: the distinct `file:line` citation count of the text
346
+ // report_source names -- null on a non-terminal status, never gated by include_report.
347
+ report_citation_rows: { type: ['integer', 'null'], minimum: 0 },
348
+ // lead-cost routing round 2 D-3: the character count of that same text (after the 64 KiB
349
+ // byte cap), null on a non-terminal status.
350
+ report_chars: { type: ['integer', 'null'], minimum: 0 },
351
+ // lead-cost routing round 2 D-3: present only when the report/last-message read threw --
352
+ // the error code (or 'read_failed'), with both counts at 0.
353
+ report_read_error: { type: 'string' },
345
354
  last_message_path: { type: 'string' },
346
355
  retention: WORKER_RETENTION_SCHEMA, poll_after_ms: { type: 'integer', minimum: 0 }, execution_profile: { type: 'object' }, // M-7 retention; WP-7 D-8 poll hint
347
356
  // Worker budget ledger (WP-3, delegate-codex-lessons §5 D-2/D-6): task-scoped provider
@@ -422,7 +431,16 @@ const WORKER_WAIT_OUTPUT_SCHEMA = {
422
431
  properties: {
423
432
  ...WORKER_STATUS_OUTPUT_SCHEMA.properties,
424
433
  waited_ms: { type: 'integer', minimum: 0 },
425
- woke_on: { type: 'string', enum: ['terminal', 'generation', 'advisory', 'timeout', 'user_input', 'route_clear'] },
434
+ // WP-1 (D-3/D-4): 'watcher' = the parked wait's watcher saw the worker stuck, looping or
435
+ // asking a question; 'watcher_unavailable' = it failed twice in a row and the lead is back
436
+ // to plain-code wakes.
437
+ woke_on: { type: 'string', enum: ['terminal', 'generation', 'advisory', 'timeout', 'user_input', 'route_clear', 'watcher', 'watcher_unavailable'] },
438
+ watcher_reason: { type: ['string', 'null'] },
439
+ watcher_decisions: { type: 'array', items: { type: 'object', additionalProperties: true } },
440
+ // WP-2 (session-stops-mid-job-2026-10-24.html §6 D-9): non-null while a worker this wait
441
+ // covered is still live -- it names that worker and says to call walle_worker_wait again
442
+ // (or walle_stop_worker); null once every worker of the wait is terminal.
443
+ lead_next_step: { type: ['string', 'null'] },
426
444
  route: { type: 'string' },
427
445
  admission: { type: ['object', 'null'], additionalProperties: true },
428
446
  worker: { type: 'object', additionalProperties: true },
@@ -435,7 +453,7 @@ const WORKER_WAIT_OUTPUT_SCHEMA = {
435
453
 
436
454
  const WORKER_WAIT_TOOL = {
437
455
  name: 'walle_worker_wait',
438
- description: 'Block until a delegated worker reaches a terminal state or starts a new generation, or until wait_ms elapses (or, with route, until that route hold ends); returns the walle_worker_status payload plus woke_on. Use this instead of sleep loops or polling the report file.',
456
+ description: 'Block until a delegated worker reaches a terminal state or starts a new generation, or until wait_ms elapses (or, with route, until that route hold ends); returns the walle_worker_status payload plus woke_on. Use this instead of sleep loops or polling the report file, or when a watcher sees the worker stuck, looping or asking a question, or when your user types.',
439
457
  inputSchema: {
440
458
  type: 'object',
441
459
  properties: {