create-walle 0.10.0 → 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 (381) hide show
  1. package/README.md +88 -41
  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 +7 -5
  6. package/package.json +10 -4
  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/capture-codex-rollout-records.js +417 -0
  11. package/template/coding-task-manager/bin/census-codex-lead-history.js +325 -0
  12. package/template/coding-task-manager/bin/claude-hook.js +7 -3
  13. package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
  14. package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
  15. package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
  16. package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
  17. package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
  18. package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
  19. package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
  20. package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
  21. package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
  22. package/template/coding-task-manager/bin/seam-replay-codex.js +301 -0
  23. package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
  24. package/template/coding-task-manager/db.js +705 -286
  25. package/template/coding-task-manager/git-utils.js +44 -0
  26. package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
  27. package/template/coding-task-manager/lib/agent-presets.js +15 -0
  28. package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
  29. package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
  30. package/template/coding-task-manager/lib/api/host-stats.js +98 -0
  31. package/template/coding-task-manager/lib/api/ingress.js +35 -12
  32. package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
  33. package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
  34. package/template/coding-task-manager/lib/api/machines.js +461 -0
  35. package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
  36. package/template/coding-task-manager/lib/api/providers.js +53 -6
  37. package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
  38. package/template/coding-task-manager/lib/api/sessions.js +160 -8
  39. package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
  40. package/template/coding-task-manager/lib/api/worktrees.js +38 -37
  41. package/template/coding-task-manager/lib/approval-drift.js +16 -1
  42. package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
  43. package/template/coding-task-manager/lib/approval-scope.js +68 -4
  44. package/template/coding-task-manager/lib/auth-context.js +32 -2
  45. package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
  46. package/template/coding-task-manager/lib/auth-rules.js +186 -45
  47. package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
  48. package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
  49. package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
  50. package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
  51. package/template/coding-task-manager/lib/claude-session-title.js +6 -30
  52. package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
  53. package/template/coding-task-manager/lib/client-diag-handler.js +25 -0
  54. package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
  55. package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
  56. package/template/coding-task-manager/lib/codex-history-terminal-renderer.js +33 -33
  57. package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
  58. package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
  59. package/template/coding-task-manager/lib/codex-local-links.js +267 -0
  60. package/template/coding-task-manager/lib/codex-markdown-lists.js +160 -0
  61. package/template/coding-task-manager/lib/codex-native-history-prefix.js +4 -0
  62. package/template/coding-task-manager/lib/codex-paths.js +2 -1
  63. package/template/coding-task-manager/lib/codex-source-handoff.js +186 -0
  64. package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
  65. package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
  66. package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
  67. package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
  68. package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
  69. package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
  70. package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
  71. package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
  72. package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
  73. package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
  74. package/template/coding-task-manager/lib/db-domains.js +21 -2
  75. package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
  76. package/template/coding-task-manager/lib/db-owner-worker-client.js +22 -0
  77. package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
  78. package/template/coding-task-manager/lib/feature-manifest.js +1 -3
  79. package/template/coding-task-manager/lib/fs-watcher.js +3 -2
  80. package/template/coding-task-manager/lib/harness-history-transition.js +41 -14
  81. package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
  82. package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
  83. package/template/coding-task-manager/lib/headless-term-service.js +60 -23
  84. package/template/coding-task-manager/lib/history-transition-read-op.js +179 -23
  85. package/template/coding-task-manager/lib/host-control-dial.js +167 -0
  86. package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
  87. package/template/coding-task-manager/lib/host-handoff.js +51 -13
  88. package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
  89. package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
  90. package/template/coding-task-manager/lib/host-stats.js +328 -0
  91. package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
  92. package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
  93. package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
  94. package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
  95. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
  96. package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
  97. package/template/coding-task-manager/lib/instance-manifest.js +13 -1
  98. package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
  99. package/template/coding-task-manager/lib/kernel/diag-log.js +12 -0
  100. package/template/coding-task-manager/lib/launch-model-override.js +160 -1
  101. package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
  102. package/template/coding-task-manager/lib/lead-model-observer.js +17 -2
  103. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
  104. package/template/coding-task-manager/lib/lead-model-switch.js +11 -2
  105. package/template/coding-task-manager/lib/lead-model-transition-runtime.js +42 -4
  106. package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
  107. package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
  108. package/template/coding-task-manager/lib/login-tripwire.js +18 -0
  109. package/template/coding-task-manager/lib/machine-pairing.js +238 -0
  110. package/template/coding-task-manager/lib/machine-pool.js +409 -0
  111. package/template/coding-task-manager/lib/machine-registry.js +749 -0
  112. package/template/coding-task-manager/lib/machine-self.js +66 -0
  113. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +56 -4
  114. package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
  115. package/template/coding-task-manager/lib/media-journal.js +84 -27
  116. package/template/coding-task-manager/lib/media-store-options.js +43 -15
  117. package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
  118. package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
  119. package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +16 -3
  120. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  121. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  122. package/template/coding-task-manager/lib/model-proxy/collaboration-codec.js +94 -0
  123. package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
  124. package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
  125. package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
  126. package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
  127. package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
  128. package/template/coding-task-manager/lib/model-proxy/responses-face.js +24 -2
  129. package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
  130. package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
  131. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  132. package/template/coding-task-manager/lib/native-history-control.js +62 -14
  133. package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
  134. package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
  135. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
  136. package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
  137. package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
  138. package/template/coding-task-manager/lib/prompt-admission-trace.js +69 -0
  139. package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
  140. package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
  141. package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
  142. package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
  143. package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
  144. package/template/coding-task-manager/lib/raw-history-block.js +3 -1
  145. package/template/coding-task-manager/lib/read-pool-client.js +36 -24
  146. package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
  147. package/template/coding-task-manager/lib/read-pool-telemetry.js +14 -5
  148. package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
  149. package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
  150. package/template/coding-task-manager/lib/remote-command-context.js +12 -0
  151. package/template/coding-task-manager/lib/remote-command-runtime.js +18 -5
  152. package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
  153. package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
  154. package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
  155. package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
  156. package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
  157. package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
  158. package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
  159. package/template/coding-task-manager/lib/restart-preflight.js +9 -2
  160. package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
  161. package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
  162. package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
  163. package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
  164. package/template/coding-task-manager/lib/resume-cwd.js +13 -0
  165. package/template/coding-task-manager/lib/resume-from-store.js +12 -1
  166. package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
  167. package/template/coding-task-manager/lib/resume-preflight.js +21 -16
  168. package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
  169. package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
  170. package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
  171. package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
  172. package/template/coding-task-manager/lib/root-prompt-admission.js +43 -11
  173. package/template/coding-task-manager/lib/root-prompt-flow.js +175 -38
  174. package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
  175. package/template/coding-task-manager/lib/server-memory-sampler-runtime.js +10 -0
  176. package/template/coding-task-manager/lib/server-memory-sampler.js +41 -2
  177. package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
  178. package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
  179. package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
  180. package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
  181. package/template/coding-task-manager/lib/session-create/identity.js +20 -1
  182. package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
  183. package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
  184. package/template/coding-task-manager/lib/session-create/provider-process.js +38 -39
  185. package/template/coding-task-manager/lib/session-create/response.js +30 -12
  186. package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
  187. package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
  188. package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
  189. package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
  190. package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
  191. package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
  192. package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
  193. package/template/coding-task-manager/lib/session-host-manager.js +152 -206
  194. package/template/coding-task-manager/lib/session-jobs.js +17 -9
  195. package/template/coding-task-manager/lib/session-lifecycle.js +20 -50
  196. package/template/coding-task-manager/lib/session-model-action-preconditions.js +68 -3
  197. package/template/coding-task-manager/lib/session-resource-sampler.js +67 -9
  198. package/template/coding-task-manager/lib/session-standup.js +67 -4
  199. package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
  200. package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
  201. package/template/coding-task-manager/lib/session-status.js +2 -2
  202. package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
  203. package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
  204. package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
  205. package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
  206. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
  207. package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
  208. package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
  209. package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
  210. package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
  211. package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
  212. package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
  213. package/template/coding-task-manager/lib/transcript-link.js +77 -0
  214. package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
  215. package/template/coding-task-manager/lib/transcript-source.js +8 -3
  216. package/template/coding-task-manager/lib/transcript-store.js +40 -0
  217. package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
  218. package/template/coding-task-manager/lib/typing-interaction-trace.js +136 -2
  219. package/template/coding-task-manager/lib/typing-worker-stages.js +208 -1
  220. package/template/coding-task-manager/lib/usage-ledger/claude-parse.js +53 -2
  221. package/template/coding-task-manager/lib/usage-ledger/ingest.js +13 -4
  222. package/template/coding-task-manager/lib/usage-ledger/read.js +14 -0
  223. package/template/coding-task-manager/lib/usage-ledger/schema.js +49 -1
  224. package/template/coding-task-manager/lib/usage-ledger/store.js +337 -6
  225. package/template/coding-task-manager/lib/usage-ledger/task-read.js +123 -5
  226. package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
  227. package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
  228. package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
  229. package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
  230. package/template/coding-task-manager/lib/ws/input.js +1 -0
  231. package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
  232. package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
  233. package/template/coding-task-manager/lib/ws-router.js +68 -1
  234. package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
  235. package/template/coding-task-manager/oauth-proxy.js +99 -55
  236. package/template/coding-task-manager/package-lock.json +24 -0
  237. package/template/coding-task-manager/package.json +3 -0
  238. package/template/coding-task-manager/protocol/message-table.json +0 -10
  239. package/template/coding-task-manager/protocol/messages.js +0 -10
  240. package/template/coding-task-manager/protocol/ws-schema.json +0 -20
  241. package/template/coding-task-manager/protocol/ws-schema.md +0 -2
  242. package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
  243. package/template/coding-task-manager/providers/codex-mcp.js +14 -5
  244. package/template/coding-task-manager/public/css/app.css +279 -2
  245. package/template/coding-task-manager/public/css/reviews.css +4 -0
  246. package/template/coding-task-manager/public/index.html +297 -240
  247. package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
  248. package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
  249. package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
  250. package/template/coding-task-manager/public/js/background-work.js +13 -7
  251. package/template/coding-task-manager/public/js/client-health-core.js +81 -0
  252. package/template/coding-task-manager/public/js/client-health-main.js +61 -0
  253. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
  254. package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
  255. package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
  256. package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
  257. package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
  258. package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
  259. package/template/coding-task-manager/public/js/history-scroller.js +187 -9
  260. package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
  261. package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
  262. package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
  263. package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -4
  264. package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
  265. package/template/coding-task-manager/public/js/protocol.js +0 -10
  266. package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
  267. package/template/coding-task-manager/public/js/reviews.js +7 -3
  268. package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
  269. package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
  270. package/template/coding-task-manager/public/js/row-join.js +18 -3
  271. package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
  272. package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
  273. package/template/coding-task-manager/public/js/session-usage-view.js +1 -0
  274. package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
  275. package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
  276. package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
  277. package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
  278. package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
  279. package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
  280. package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
  281. package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
  282. package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
  283. package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
  284. package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
  285. package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -3
  286. package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
  287. package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
  288. package/template/coding-task-manager/public/js/walle-session.js +30 -7
  289. package/template/coding-task-manager/public/m/app.css +226 -8
  290. package/template/coding-task-manager/public/m/app.js +502 -9
  291. package/template/coding-task-manager/public/m/index.html +38 -1
  292. package/template/coding-task-manager/public/m/machines.js +1313 -0
  293. package/template/coding-task-manager/public/m/pool-failover.js +391 -0
  294. package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
  295. package/template/coding-task-manager/public/m/sw.js +5 -0
  296. package/template/coding-task-manager/queue-engine.js +13 -0
  297. package/template/coding-task-manager/server/approvals.js +5 -6
  298. package/template/coding-task-manager/server/boot.js +136 -17
  299. package/template/coding-task-manager/server.js +964 -425
  300. package/template/coding-task-manager/session-integrity.js +7 -9
  301. package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
  302. package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
  303. package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
  304. package/template/coding-task-manager/workers/session-host-process.js +14 -3
  305. package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
  306. package/template/coding-task-manager/workers/worktree-worker.js +34 -0
  307. package/template/package-lock.json +2 -2
  308. package/template/package.json +1 -1
  309. package/template/scripts/conversation-storage-v2-migration.js +78 -11
  310. package/template/shared/conversation-storage-v2-contract.js +42 -6
  311. package/template/shared/fixtures/worker-status-payloads.json +392 -0
  312. package/template/shared/git-remote-key.js +83 -0
  313. package/template/shared/host-identity.js +98 -2
  314. package/template/shared/owner-write-safety.js +135 -0
  315. package/template/shared/public-runtime.js +13 -5
  316. package/template/shared/release-schema.json +3 -2
  317. package/template/shared/report-citations.js +14 -0
  318. package/template/shared/sqlite-write-lock.js +361 -223
  319. package/template/shared/storage/contract.js +35 -3
  320. package/template/wall-e/agent.js +21 -0
  321. package/template/wall-e/api-walle.js +5 -0
  322. package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
  323. package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
  324. package/template/wall-e/brain/helper-rules-schema.js +129 -0
  325. package/template/wall-e/brain/migrations.js +51 -22
  326. package/template/wall-e/brain/model-deployment-facts.js +31 -1
  327. package/template/wall-e/brain/owner-call-whitelist.js +16 -0
  328. package/template/wall-e/brain/schema-ensures.js +22 -0
  329. package/template/wall-e/brain.js +1 -1
  330. package/template/wall-e/coding/host-capabilities.js +2 -2
  331. package/template/wall-e/coding/journal-dispatcher.js +51 -0
  332. package/template/wall-e/coding/session-journal.js +28 -40
  333. package/template/wall-e/coding-orchestrator.js +23 -26
  334. package/template/wall-e/fly.toml +1 -1
  335. package/template/wall-e/http/auth.js +18 -1
  336. package/template/wall-e/http/blog-views-api.js +318 -0
  337. package/template/wall-e/http/helper-rules-api.js +321 -0
  338. package/template/wall-e/http/model-proxy-providers.js +3 -1
  339. package/template/wall-e/http/telemetry-api.js +12 -0
  340. package/template/wall-e/lib/blog-view-policy.js +88 -0
  341. package/template/wall-e/lib/blog-view-store.js +169 -0
  342. package/template/wall-e/lib/client-config-lock.js +2 -1
  343. package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
  344. package/template/wall-e/lib/error-kinds.js +10 -1
  345. package/template/wall-e/lib/feature-manifest.js +1 -1
  346. package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
  347. package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
  348. package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
  349. package/template/wall-e/lib/helper-rules/grade.js +277 -0
  350. package/template/wall-e/lib/helper-rules/label.js +247 -0
  351. package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
  352. package/template/wall-e/lib/helper-rules/store.js +667 -0
  353. package/template/wall-e/lib/mcp-integration.js +5 -6
  354. package/template/wall-e/lib/runtime-worker-pool.js +1 -0
  355. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
  356. package/template/wall-e/lib/telemetry-db-schema.js +37 -0
  357. package/template/wall-e/lib/telemetry-process-client.js +76 -1
  358. package/template/wall-e/llm/default-fallback.js +4 -0
  359. package/template/wall-e/llm/model-catalog.js +13 -4
  360. package/template/wall-e/llm/model-roles.js +6 -0
  361. package/template/wall-e/llm/portkey-http.js +150 -0
  362. package/template/wall-e/llm/portkey-sync.js +15 -145
  363. package/template/wall-e/llm/worker-mcp-routing.js +61 -0
  364. package/template/wall-e/llm/worker-route.js +43 -1
  365. package/template/wall-e/loops/brain-custom-writes.js +45 -0
  366. package/template/wall-e/mcp-protocol-contract.js +41 -1
  367. package/template/wall-e/mcp-server.js +13 -2
  368. package/template/wall-e/mcp-worker-schemas.js +20 -2
  369. package/template/wall-e/memory/ctm-context-client.js +25 -5
  370. package/template/wall-e/shared/release-schema.json +3 -2
  371. package/template/wall-e/shared/sqlite-write-lock.js +361 -223
  372. package/template/wall-e/shared/storage/contract.js +35 -3
  373. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  374. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
  375. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
  376. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
  377. package/template/wall-e/tools/local-tools.js +12 -5
  378. package/template/wall-e/workers/brain-owner-worker.js +9 -0
  379. package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
  380. package/template/wall-e/workers/telemetry-worker.js +8 -0
  381. package/template/coding-task-manager/lib/giant-freeze-park.js +0 -31
@@ -56,6 +56,16 @@ var _modelsAgentStatusPromise = null;
56
56
  // 2 s setTimeout while a flow is open — never an interval, and it stops itself as soon
57
57
  // as no flow is open, so an idle models page costs nothing.
58
58
  var _modelsProviderLogin = null;
59
+ // WP-3 (docs/remote-agent-sign-in-from-hub-browser-2026-10-25.html §5 D-7/D-9): the last
60
+ // login-state payload read for a REMOTE machine (keyed by instance id) — the Machines page's
61
+ // machine-scoped poll keeps its box's truth here, deliberately apart from _modelsProviderLogin so a
62
+ // box's sign-in state can never be painted as this machine's own card.
63
+ var _modelsProviderLoginMachines = {};
64
+ // WP-2 fix: the last payload fingerprint the card was painted from. The 2 s poll
65
+ // re-reads login-state every tick, but the probe only moves `account.checkedAt`
66
+ // between ticks — repainting for that alone rebuilt the whole section (and with it
67
+ // the code box the user was typing into) every 2 s. See _modelsProviderLoginRenderKey.
68
+ var _modelsProviderLoginLastRenderKey = '';
59
69
  var _modelsProviderLoginPollTimer = null;
60
70
  var _modelsProviderLoginPolling = false;
61
71
  var _MODELS_PROVIDER_LOGIN_OPEN_STATUSES = ['starting', 'running', 'awaiting_user'];
@@ -97,6 +107,10 @@ var _routingSuggestionHistory = [];
97
107
  var _routingBanditHistory = [];
98
108
  var _routingBanditReadiness = null;
99
109
  var _routingLaunchState = null;
110
+ // WP-7 (lead-cost routing §6.9): the "Helper rules" pill's read model — the Wall-E cell list and
111
+ // the one-line error the panel shows instead of an alert.
112
+ var _helperRulesCells = null;
113
+ var _helperRulesError = null;
100
114
 
101
115
  // AI Providers → Usage tab: token/cost consumption for the default model, split by
102
116
  // process (Wall-E chat vs CTM) and kept separate from the coding-agent model.
@@ -1857,6 +1871,9 @@ function _ensureModelsAgentStatus(force) {
1857
1871
  ]).then(function(results) {
1858
1872
  _modelsAutoUpdate = results[3];
1859
1873
  if (results[4] && results[4].providers) _modelsProviderLogin = results[4];
1874
+ // WP-2 fix (A): this fan-out renders the card itself (below), so remember what it
1875
+ // painted — the first poll after a page load must not repaint for nothing.
1876
+ _modelsProviderLoginLastRenderKey = _modelsProviderLoginRenderKey(_modelsProviderLogin);
1860
1877
  _modelsCodingAgentMigrateLocalDismissals(results[1]);
1861
1878
  _modelsAgentStatus = {
1862
1879
  available: results[0] || { agents: {} },
@@ -1909,18 +1926,34 @@ function _modelsProviderLoginFlowIsOpen(flow) {
1909
1926
  return !!flow && _MODELS_PROVIDER_LOGIN_OPEN_STATUSES.indexOf(String(flow.status || '')) >= 0;
1910
1927
  }
1911
1928
 
1912
- // The Account row: signed-in email + plan, "Not signed in", or the probe's reason.
1929
+ // The Account row (R-4): signed-in email + plan, "Not signed in", or -- when the
1930
+ // probe could not answer (signedIn === null: non-primary instance, hermetic env,
1931
+ // timeout, CLI missing) -- plain "Unknown". The probe's machine reason
1932
+ // ('probe_unavailable', 'cli_missing', ...) is a diagnostic, never user copy: it
1933
+ // rides in the value's title via _modelsCodingAgentAccountTitle below, exactly
1934
+ // like the status pill's own title.
1913
1935
  function _modelsCodingAgentAccountText(preset) {
1914
1936
  var entry = _modelsProviderLoginEntry(preset);
1915
- if (!entry) return 'Unknown (unavailable)';
1916
- var account = entry.account || null;
1917
- if (!account) return 'Unknown (no probe)';
1918
- if (account.signedIn === true) {
1937
+ var account = (entry && entry.account) || null;
1938
+ if (account && account.signedIn === true) {
1919
1939
  var who = account.email ? String(account.email) : 'signed in';
1920
1940
  return account.plan ? (who + ' · ' + account.plan) : who;
1921
1941
  }
1922
- if (account.signedIn === false) return 'Not signed in';
1923
- return 'Unknown (' + (account.reason ? String(account.reason) : 'unknown') + ')';
1942
+ if (account && account.signedIn === false) return 'Not signed in';
1943
+ return 'Unknown';
1944
+ }
1945
+
1946
+ // The diagnostic behind an "Unknown" account: which machine reason kept the probe
1947
+ // from answering, plus the probe's own detail when it has one. Empty when the row
1948
+ // has nothing to explain (a probe that answered, signed in or not).
1949
+ function _modelsCodingAgentAccountTitle(preset) {
1950
+ var entry = _modelsProviderLoginEntry(preset);
1951
+ if (!entry) return 'Login probe: unavailable';
1952
+ var account = entry.account || null;
1953
+ if (!account) return 'Login probe: no probe';
1954
+ if (typeof account.signedIn === 'boolean') return '';
1955
+ var reason = account.reason ? String(account.reason) : 'unknown';
1956
+ return 'Login probe: ' + reason + (account.detail ? ' — ' + String(account.detail) : '');
1924
1957
  }
1925
1958
 
1926
1959
  function _modelsCodingAgentAccountColor(preset) {
@@ -1930,17 +1963,154 @@ function _modelsCodingAgentAccountColor(preset) {
1930
1963
  return 'var(--fg-dim,#565f89)';
1931
1964
  }
1932
1965
 
1933
- // Only a URL the browser parses as https may become an href — the CLI prints this
1934
- // string, and the card must never turn it into a link of another scheme.
1966
+ // D-1 on the client (S-10, defence in depth): the CLI also opens an automatic
1967
+ // browser on ITS own machine, so a loopback URL is that machine's link, never one a
1968
+ // remote (hub / phone) viewer of this card can follow. Both halves of the URL are
1969
+ // checked — the link host and, when present, the OAuth redirect_uri it carries.
1970
+ //
1971
+ // true when `value` parses to a loopback host. A value that is not a URL at all is
1972
+ // not loopback: it just cannot promote an href (the caller drops it for its scheme).
1973
+ function _modelsProviderLoginLoopbackHost(value) {
1974
+ var hosts = ['localhost', '127.0.0.1', '::1', '[::1]'];
1975
+ var host = '';
1976
+ try {
1977
+ host = new URL(String(value || '')).hostname;
1978
+ } catch (e) {
1979
+ return false;
1980
+ }
1981
+ return hosts.indexOf(String(host).toLowerCase()) >= 0;
1982
+ }
1983
+
1984
+ // Only a URL the browser parses as https — and whose host, and redirect_uri host,
1985
+ // are not loopback — may become an href. The CLI prints this string; the card must
1986
+ // never turn it into a link of another scheme, nor hand out its own machine's link.
1935
1987
  function _modelsProviderLoginUrl(value) {
1936
1988
  try {
1937
1989
  var parsed = new URL(String(value || ''));
1938
- return parsed.protocol === 'https:' ? parsed.href : '';
1990
+ if (parsed.protocol !== 'https:') return '';
1991
+ if (_modelsProviderLoginLoopbackHost(parsed.href)) return '';
1992
+ var redirect = parsed.searchParams.get('redirect_uri');
1993
+ if (redirect && _modelsProviderLoginLoopbackHost(redirect)) return '';
1994
+ return parsed.href;
1939
1995
  } catch (e) {
1940
1996
  return '';
1941
1997
  }
1942
1998
  }
1943
1999
 
2000
+ function _modelsProviderLoginHintStyle() {
2001
+ return 'font-size:11px;color:var(--fg-dim,#565f89);';
2002
+ }
2003
+
2004
+ // §9 S-9: the server's refusal ids are machine copy — the card says what the user
2005
+ // can do about each one. Anything unrecognised still names its id, never a blank.
2006
+ function _modelsProviderLoginErrorText(errorId) {
2007
+ var id = String(errorId === null || errorId === undefined ? '' : errorId);
2008
+ if (id === 'code_format_invalid') return "That doesn't look like a sign-in code. Paste the code, or the page address, from the sign-in page.";
2009
+ if (id === 'code_state_mismatch') return 'That code is from a different sign-in. Use the link above and try again.';
2010
+ if (id === 'code_from_loopback_redirect') return 'That page came from a localhost link. Use the Open sign-in page link above.';
2011
+ if (id === 'login_tab_gone' || id === 'provider_login_not_open') return 'The sign-in tab closed. Start Sign in again.';
2012
+ if (id === 'code_entry_not_supported') return 'This sign-in does not take a code here.';
2013
+ return 'Could not send the code (' + id + ').';
2014
+ }
2015
+
2016
+ // D-3/D-7: the way the code travels decides the fragment — never the provider id.
2017
+ // 'browser' (Codex) shows the one-time code the tab printed for the browser,
2018
+ // 'paste' (Claude) takes the code the browser shows back, '' waits for the page.
2019
+ // Same markup and same labels on every host.
2020
+ function _modelsProviderLoginCodeEntryHtml(flow) {
2021
+ var entry = (flow && flow.codeEntry) || '';
2022
+ var hintStyle = _modelsProviderLoginHintStyle();
2023
+ if (entry === 'browser') {
2024
+ return '<code data-sign-in-user-code style="font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;background:var(--bg,#1a1b26);border:1px solid var(--border,#414868);border-radius:4px;padding:2px 6px;color:var(--fg,#c0caf5);">'
2025
+ + escHtml((flow && flow.userCode) || '') + '</code>'
2026
+ + '<button type="button" data-sign-in-copy-code style="' + _modelsProviderLoginButtonStyle(false) + '">Copy code</button>'
2027
+ + '<span data-sign-in-hint style="' + hintStyle + '">Enter this code on the sign-in page.</span>';
2028
+ }
2029
+ if (entry === 'paste') {
2030
+ return '<input data-sign-in-code type="text" inputmode="text" autocomplete="off" spellcheck="false" placeholder="Paste the code from the sign-in page" style="font-size:11px;padding:4px 7px;border-radius:5px;border:1px solid var(--border,#414868);background:var(--bg,#1a1b26);color:var(--fg,#c0caf5);min-width:240px;" />'
2031
+ + '<button type="button" data-sign-in-code-submit style="' + _modelsProviderLoginButtonStyle(true) + '">Submit code</button>'
2032
+ + '<span data-sign-in-hint style="' + hintStyle + '">After you sign in, the page shows a code. Paste it here (the page address works too).</span>'
2033
+ + '<span data-sign-in-error style="font-size:11px;color:#f7768e;"></span>';
2034
+ }
2035
+ return '<span data-sign-in-hint style="' + hintStyle + '">Waiting for the sign-in page…</span>';
2036
+ }
2037
+
2038
+ // WP-3 (docs/remote-agent-sign-in-from-hub-browser-2026-10-25.html §5 D-7/D-9, §6, S-13):
2039
+ // the machine scope. The SAME `&machine=<id>` fragment the picker routes carry
2040
+ // (machineQueryParam(), public/js/core/session-commands.js) — spelled here rather than called,
2041
+ // because machineQueryParam() reads the PICKER's selection and the machine a card is scoped to is
2042
+ // an explicit argument, not that selection. '' produces nothing, so every unscoped URL is
2043
+ // byte-identical to its pre-WP-3 self.
2044
+ //
2045
+ // It is appended to the URL _modelsApiUrl() RETURNS, never to its argument: _modelsApiUrl appends
2046
+ // `?token=`/`&token=` to whatever it is handed, so a fragment spliced into the pathname would end
2047
+ // up before the token and the query would read `…/login&machine=x?token=…` — a route the hub does
2048
+ // not serve. `&machine=` is always well-formed here because _modelsApiUrl always emits a query.
2049
+ function _modelsProviderLoginMachineScope(machineId) {
2050
+ var id = machineId === null || machineId === undefined ? '' : String(machineId).trim();
2051
+ return id ? '&machine=' + encodeURIComponent(id) : '';
2052
+ }
2053
+
2054
+ // One POST, one meaning: hand the pasted code to the sign-in tab (§5 D-4). The value
2055
+ // is never logged, toasted or written anywhere but the input itself, and the button
2056
+ // stays disabled until the answer arrives (so a second Enter cannot double-post).
2057
+ // WP-3: `opts.machineId` (S-13) targets the box that owns the flow — the hub forwards to it.
2058
+ function _modelsProviderLoginSubmitCode(providerId, inputEl, errorEl, btn, opts) {
2059
+ var options = opts || {};
2060
+ if (!providerId || !inputEl) return Promise.resolve();
2061
+ var submitBtn = btn || null;
2062
+ var errorSpan = errorEl || null;
2063
+ if (submitBtn && submitBtn.disabled) return Promise.resolve();
2064
+ if (submitBtn) submitBtn.disabled = true;
2065
+ if (errorSpan) errorSpan.textContent = '';
2066
+ var refuse = function(errorId) {
2067
+ if (errorSpan) errorSpan.textContent = _modelsProviderLoginErrorText(errorId);
2068
+ if (submitBtn) submitBtn.disabled = false;
2069
+ };
2070
+ return fetch(_modelsApiUrl('/api/providers/' + encodeURIComponent(providerId) + '/login/code')
2071
+ + _modelsProviderLoginMachineScope(options.machineId), {
2072
+ method: 'POST',
2073
+ headers: { 'Content-Type': 'application/json' },
2074
+ body: JSON.stringify({ code: String(inputEl.value || '') }),
2075
+ })
2076
+ .then(
2077
+ function(r) {
2078
+ return r.json().then(
2079
+ function(data) { return { ok: r.ok, data: data }; },
2080
+ function() { return { ok: r.ok, data: null }; },
2081
+ );
2082
+ },
2083
+ function() { return null; },
2084
+ )
2085
+ .then(function(result) {
2086
+ if (!result) {
2087
+ refuse('network_error');
2088
+ return null;
2089
+ }
2090
+ if (result.ok) {
2091
+ if (typeof toast === 'function') toast('Code sent to the sign-in tab');
2092
+ inputEl.value = '';
2093
+ return _modelsProviderLoginRefresh(options);
2094
+ }
2095
+ refuse((result.data && result.data.error) || 'unknown_error');
2096
+ return null;
2097
+ });
2098
+ }
2099
+
2100
+ // The clipboard is the browser's, and its refusal is a label, never an exception
2101
+ // escaping to the console. No timer: the label holds until the next render.
2102
+ function _modelsProviderLoginCopyCode(code, btn) {
2103
+ var label = function(text) { if (btn) btn.textContent = text; };
2104
+ try {
2105
+ navigator.clipboard.writeText(String(code || '')).then(
2106
+ function() { label('Copied'); },
2107
+ function() { label('Copy failed'); },
2108
+ );
2109
+ } catch (e) {
2110
+ label('Copy failed');
2111
+ }
2112
+ }
2113
+
1944
2114
  function _modelsProviderLoginButtonStyle(primary) {
1945
2115
  return primary
1946
2116
  ? 'padding:5px 10px;border-radius:5px;border:1px solid rgba(224,175,104,0.55);background:rgba(224,175,104,0.12);color:#e0af68;cursor:pointer;font-size:11px;font-weight:700;'
@@ -1948,9 +2118,17 @@ function _modelsProviderLoginButtonStyle(primary) {
1948
2118
  }
1949
2119
 
1950
2120
  // The card's sign-in action area. Same markup and same labels on every host (R-4).
1951
- function _modelsProviderLoginActionsHtml(preset) {
2121
+ //
2122
+ // WP-3 (S-13): `flowOverride` (undefined = read the card's own payload) and `opts.machineId` let a
2123
+ // DIFFERENT surface — the Machines page — render this same block for a REMOTE machine. With a
2124
+ // machineId the block is machine-scoped: the tab button is never offered (D-9: the sign-in tab
2125
+ // lives on the box, there is no local session to activate), and the caller fills the block for its
2126
+ // own row. Called with no flow and no opts (the card's own call site) the output is unchanged.
2127
+ function _modelsProviderLoginActionsHtml(preset, flowOverride, opts) {
2128
+ var options = opts || {};
2129
+ var machineId = options.machineId === null || options.machineId === undefined ? '' : String(options.machineId).trim();
1952
2130
  if (!_modelsProviderIdForPreset(preset)) return '';
1953
- var flow = _modelsProviderLoginFlowFor(preset);
2131
+ var flow = flowOverride === undefined ? _modelsProviderLoginFlowFor(preset) : flowOverride;
1954
2132
  var status = _modelsCodingAgentStatusFor(preset, _modelsCodingAgentAccountFor(preset));
1955
2133
  if (_modelsProviderLoginFlowIsOpen(flow)) {
1956
2134
  var parts = [];
@@ -1958,12 +2136,15 @@ function _modelsProviderLoginActionsHtml(preset) {
1958
2136
  if (url) {
1959
2137
  parts.push('<a data-open-sign-in-page href="' + escHtml(url) + '" target="_blank" rel="noopener" style="' + _modelsProviderLoginButtonStyle(true) + 'text-decoration:none;display:inline-block;">Open sign-in page</a>');
1960
2138
  }
1961
- parts.push('<span data-sign-in-hint style="font-size:11px;color:var(--fg-dim,#565f89);">Enter the code shown in the sign-in tab</span>');
1962
- if (flow.sessionId) {
2139
+ parts.push(_modelsProviderLoginCodeEntryHtml(flow));
2140
+ if (flow.sessionId && !machineId) {
1963
2141
  parts.push('<button type="button" data-show-sign-in-tab="' + escHtml(flow.sessionId) + '" style="' + _modelsProviderLoginButtonStyle(false) + '">Show sign-in tab</button>');
1964
2142
  }
1965
2143
  return parts.join('');
1966
2144
  }
2145
+ // A machine-scoped block with no open flow renders nothing: the Machines page owns that row's
2146
+ // own Sign in button (data-machine-sign-in), so a second one here would double the affordance.
2147
+ if (machineId) return '';
1967
2148
  var failed = !!(flow && flow.status === 'failed');
1968
2149
  var html = '';
1969
2150
  if (failed) {
@@ -1978,8 +2159,11 @@ function _modelsProviderLoginActionsHtml(preset) {
1978
2159
  return html;
1979
2160
  }
1980
2161
 
1981
- function _modelsProviderLoginHasOpenFlow() {
1982
- var providers = (_modelsProviderLogin && _modelsProviderLogin.providers) || {};
2162
+ // `login` defaults to the card's own payload, so the card's call sites are unchanged; a
2163
+ // machine-scoped poll passes the payload it just fetched for that box.
2164
+ function _modelsProviderLoginHasOpenFlow(login) {
2165
+ var source = login === undefined ? _modelsProviderLogin : login;
2166
+ var providers = (source && source.providers) || {};
1983
2167
  return Object.keys(providers).some(function(id) {
1984
2168
  return _modelsProviderLoginFlowIsOpen(providers[id] && providers[id].flow);
1985
2169
  });
@@ -1993,30 +2177,78 @@ function _modelsProviderLoginStopPoll() {
1993
2177
  _modelsProviderLoginPolling = false;
1994
2178
  }
1995
2179
 
2180
+ // WP-2 fix (A): what a login-state payload contributes to the card — per provider
2181
+ // (sorted, so key order never matters): `status`, `credential`, every field of `flow`
2182
+ // and every field of `account` EXCEPT `checkedAt`, which is the probe's own timestamp
2183
+ // and is not rendered anywhere. Two payloads with the same key paint the same card.
2184
+ function _modelsProviderLoginRenderKey(login) {
2185
+ var stable = function stableJson(value) {
2186
+ if (value === null || typeof value !== 'object') return JSON.stringify(value === undefined ? null : value);
2187
+ if (Array.isArray(value)) return '[' + value.map(stableJson).join(',') + ']';
2188
+ return '{' + Object.keys(value).sort().map(function(k) {
2189
+ return JSON.stringify(k) + ':' + stableJson(value[k]);
2190
+ }).join(',') + '}';
2191
+ };
2192
+ var providers = (login && login.providers) || {};
2193
+ return Object.keys(providers).sort().map(function(id) {
2194
+ var entry = providers[id] || {};
2195
+ var account = entry.account;
2196
+ if (account && typeof account === 'object' && !Array.isArray(account)) {
2197
+ account = Object.keys(account).reduce(function(kept, k) {
2198
+ if (k !== 'checkedAt') kept[k] = account[k];
2199
+ return kept;
2200
+ }, {});
2201
+ }
2202
+ return stable([id, entry.status, entry.credential, entry.flow, account]);
2203
+ }).join('\n');
2204
+ }
2205
+
1996
2206
  // Re-reads login-state and re-renders. Never rejects: a failed poll keeps the last
1997
- // payload, so an open flow stays visible while the next poll retries.
1998
- function _modelsProviderLoginRefresh() {
1999
- return fetch(_modelsApiUrl('/api/providers/login-state'), { cache: 'no-store' })
2207
+ // payload, so an open flow stays visible while the next poll retries. The repaint
2208
+ // happens only when the payload's fingerprint moved (WP-2 fix A): a tick that only
2209
+ // re-stamped `account.checkedAt` leaves the card — and any sign-in draft in it — alone.
2210
+ // WP-3 (S-13): `opts.machineId` reads the BOX's login-state through the hub's proxy. The payload
2211
+ // is kept per machine and never painted as this machine's own card (that is what
2212
+ // `opts.onPayload` is for: the Machines page repaints ITS row), so the two readers cannot
2213
+ // contaminate each other.
2214
+ function _modelsProviderLoginRefresh(opts) {
2215
+ var options = opts || {};
2216
+ var machineId = options.machineId === null || options.machineId === undefined ? '' : String(options.machineId).trim();
2217
+ return fetch(_modelsApiUrl('/api/providers/login-state') + _modelsProviderLoginMachineScope(options.machineId), { cache: 'no-store' })
2000
2218
  .then(function(r) { return r.ok ? r.json() : null; })
2001
2219
  .then(function(data) {
2220
+ if (machineId) {
2221
+ if (data && data.providers) _modelsProviderLoginMachines[machineId] = data;
2222
+ if (typeof options.onPayload === 'function') options.onPayload(_modelsProviderLoginMachines[machineId] || null);
2223
+ return _modelsProviderLoginMachines[machineId] || null;
2224
+ }
2002
2225
  if (data && data.providers) _modelsProviderLogin = data;
2003
- _modelsRefreshAgentStatusUi();
2226
+ var key = _modelsProviderLoginRenderKey(_modelsProviderLogin);
2227
+ if (key !== _modelsProviderLoginLastRenderKey) {
2228
+ _modelsProviderLoginLastRenderKey = key;
2229
+ _modelsRefreshAgentStatusUi();
2230
+ }
2004
2231
  return _modelsProviderLogin;
2005
2232
  })
2006
2233
  .catch(function() {
2007
- return _modelsProviderLogin;
2234
+ return machineId ? (_modelsProviderLoginMachines[machineId] || null) : _modelsProviderLogin;
2008
2235
  });
2009
2236
  }
2010
2237
 
2011
- function _modelsProviderLoginPoll() {
2238
+ // The ONE 2 s poll. `opts.machineId` scopes it to a box (WP-3): same timer, same stop condition —
2239
+ // a machine-scoped poll stops as soon as THAT box reports no open flow.
2240
+ function _modelsProviderLoginPoll(opts) {
2241
+ var options = opts || {};
2242
+ var machineId = options.machineId === null || options.machineId === undefined ? '' : String(options.machineId).trim();
2012
2243
  if (_modelsProviderLoginPollTimer) return;
2013
2244
  _modelsProviderLoginPolling = true;
2014
2245
  _modelsProviderLoginPollTimer = setTimeout(function() {
2015
2246
  _modelsProviderLoginPollTimer = null;
2016
- _modelsProviderLoginRefresh().then(function() {
2247
+ _modelsProviderLoginRefresh(options).then(function() {
2017
2248
  if (!_modelsProviderLoginPolling) return;
2018
- if (!_modelsProviderLoginHasOpenFlow()) { _modelsProviderLoginPolling = false; return; }
2019
- _modelsProviderLoginPoll();
2249
+ var source = machineId ? _modelsProviderLoginMachines[machineId] : _modelsProviderLogin;
2250
+ if (!_modelsProviderLoginHasOpenFlow(source)) { _modelsProviderLoginPolling = false; return; }
2251
+ _modelsProviderLoginPoll(options);
2020
2252
  });
2021
2253
  }, _MODELS_PROVIDER_LOGIN_POLL_MS);
2022
2254
  }
@@ -2028,11 +2260,14 @@ function _modelsProviderLoginEnsurePoll() {
2028
2260
  }
2029
2261
 
2030
2262
  // POST opens the login tab; the flow then arrives through the login-state poll.
2031
- function _modelsProviderLoginStart(preset, btn) {
2263
+ // WP-3: `opts.machineId` opens the tab on THAT box through the hub's proxy.
2264
+ function _modelsProviderLoginStart(preset, btn, opts) {
2265
+ var options = opts || {};
2032
2266
  var providerId = _modelsProviderIdForPreset(preset);
2033
2267
  if (!providerId) return Promise.resolve(null);
2034
2268
  if (btn) { btn.disabled = true; btn.textContent = 'Opening...'; }
2035
- return fetch(_modelsApiUrl('/api/providers/' + encodeURIComponent(providerId) + '/login'), {
2269
+ return fetch(_modelsApiUrl('/api/providers/' + encodeURIComponent(providerId) + '/login')
2270
+ + _modelsProviderLoginMachineScope(options.machineId), {
2036
2271
  method: 'POST',
2037
2272
  headers: { 'Content-Type': 'application/json' },
2038
2273
  body: '{}',
@@ -2046,8 +2281,8 @@ function _modelsProviderLoginStart(preset, btn) {
2046
2281
  if (typeof toast === 'function') toast('Could not start sign-in: ' + reason, { type: 'error', duration: 5000 });
2047
2282
  }
2048
2283
  _modelsProviderLoginPolling = true;
2049
- return _modelsProviderLoginRefresh().then(function() {
2050
- if (_modelsProviderLoginHasOpenFlow()) _modelsProviderLoginPoll();
2284
+ return _modelsProviderLoginRefresh(options).then(function() {
2285
+ if (_modelsProviderLoginHasOpenFlow(options.machineId ? _modelsProviderLoginMachines[String(options.machineId).trim()] : undefined)) _modelsProviderLoginPoll(options);
2051
2286
  else _modelsProviderLoginPolling = false;
2052
2287
  return result;
2053
2288
  });
@@ -2903,7 +3138,7 @@ function _routingConfirm(title, detail, acceptLabel) {
2903
3138
 
2904
3139
  function _routingWorkflowPills() {
2905
3140
  return '<div role="tablist" aria-label="Routing views" style="display:flex;gap:4px;margin-bottom:14px;overflow-x:auto;">' +
2906
- [['workflows','Workflows'],['policies','Policies'],['measurements','Measurements']].map(function(item) {
3141
+ [['workflows','Workflows'],['policies','Policies'],['measurements','Measurements'],['helper-rules','Helper rules']].map(function(item) {
2907
3142
  var active = _routingWorkflowPill === item[0];
2908
3143
  return '<button type="button" data-routing-pill="' + item[0] + '" style="padding:7px 12px;border-radius:6px;border:1px solid ' + (active ? 'var(--accent,#7aa2f7)' : 'var(--border,#414868)') + ';background:' + (active ? 'rgba(122,162,247,.16)' : 'transparent') + ';color:' + (active ? '#7aa2f7' : 'var(--fg-dim,#565f89)') + ';font-weight:700;cursor:pointer;white-space:nowrap;">' + item[1] + '</button>';
2909
3144
  }).join('') + '</div>';
@@ -3158,15 +3393,200 @@ function _routingMeasurementsMarkup() {
3158
3393
  '<div style="margin-top:14px;"><strong style="font-size:12px;">Suggestions</strong>' + (suggestions || '<div style="font-size:11px;color:var(--fg-dim,#565f89);margin-top:5px;">Suggestions appear after two arms each have at least 30 measured runs.</div>') + (audit?'<div style="margin-top:8px;">'+audit+'</div>':'') + '</div>' + banditPanel;
3159
3394
  }
3160
3395
 
3396
+ // WP-7 (design §6.9): the "Helper rules" panel — one row per rule cell as Wall-E reports it.
3397
+ // The rule evaluator (Wall-E, lib/helper-rules/store.js) owns every write; this panel only reads
3398
+ // and asks: Veto / Enable go through _routingConfirm and then straight to the API (D-18).
3399
+ var HELPER_RULE_STATE_COLOR = { on: '#9ece6a', probe: '#e0af68', off: '#f7768e' };
3400
+ // WP-6 (§6.9): the panel's outcome disclosure, keyed by cell. An entry exists only while the row is
3401
+ // open: { rows: array|null (null = in flight), error: string|null }. Session-only, never persisted.
3402
+ var _helperRulesOutcomes = {};
3403
+
3404
+ function _helperRulesPct(value) {
3405
+ var n = Number(value);
3406
+ return (value == null || !isFinite(n)) ? '-' : (n * 100).toFixed(1) + '%';
3407
+ }
3408
+
3409
+ // WP-6 (design §6.9, D-7): the rate column reports the measurability the evaluator stores
3410
+ // (`collecting` = "not enough measured rows to judge") instead of always showing a percentage, and
3411
+ // names the rows that measure nothing so a fallback rate is never read off an all-unmeasured window.
3412
+ function _helperRulesCount(metrics, key) {
3413
+ return Math.max(0, Number(metrics[key] || 0) || 0);
3414
+ }
3415
+
3416
+ function _helperRulesRate(cell) {
3417
+ var metrics = cell.metrics || {};
3418
+ if (cell.collecting) {
3419
+ var collecting = ['collecting'];
3420
+ if (cell.kind === 'explore' && _helperRulesCount(metrics, 'unmeasured') > 0) collecting.push('unmeasured ' + _helperRulesCount(metrics, 'unmeasured'));
3421
+ return collecting.join(' · ');
3422
+ }
3423
+ if (cell.kind === 'explore') {
3424
+ var parts = ['fallback ' + _helperRulesPct(metrics.fallback_rate)];
3425
+ if (_helperRulesCount(metrics, 'unmeasured') > 0) parts.push('unmeasured ' + _helperRulesCount(metrics, 'unmeasured'));
3426
+ if (_helperRulesCount(metrics, 'abandoned') > 0) parts.push('abandoned ' + _helperRulesCount(metrics, 'abandoned'));
3427
+ if (Number(metrics.graded || 0) >= 1) parts.push('sufficient ' + _helperRulesPct(metrics.sufficient_rate));
3428
+ if (_helperRulesCount(metrics, 'grade_unavailable') > 0) parts.push('grade unavailable ' + _helperRulesCount(metrics, 'grade_unavailable'));
3429
+ return parts.join(' · ');
3430
+ }
3431
+ var watch = [_helperRulesPct(metrics.precision)];
3432
+ if (typeof metrics.watcher_usd_window === 'number' && isFinite(metrics.watcher_usd_window)) {
3433
+ watch.push('$' + metrics.watcher_usd_window.toFixed(4) + ' watched');
3434
+ }
3435
+ return watch.join(' · ');
3436
+ }
3437
+
3438
+ function _helperRulesStateChip(cell) {
3439
+ var state = HELPER_RULE_STATE_COLOR[cell.state] ? cell.state : 'on';
3440
+ return '<span style="color:' + HELPER_RULE_STATE_COLOR[state] + ';font-weight:700;white-space:nowrap;">' + escHtml(state) + (cell.user_veto ? ' · vetoed' : '') + '</span>';
3441
+ }
3442
+
3443
+ function _helperRulesLastEvent(cell) {
3444
+ var event = cell.last_event;
3445
+ if (!event) return '<span style="color:var(--fg-dim,#565f89);">-</span>';
3446
+ return escHtml([event.to_state, event.reason, event.created_at].filter(function(part) { return part != null && part !== ''; }).join(' · '));
3447
+ }
3448
+
3449
+ function _helperRulesActions(cell) {
3450
+ var key = escHtml(cell.cell_key);
3451
+ var html = '';
3452
+ if (cell.state !== 'off' || Number(cell.user_veto) === 0) {
3453
+ html += '<button type="button" class="btn small" data-helper-cell="' + key + '" data-helper-action="veto">Veto</button>';
3454
+ }
3455
+ if (Number(cell.user_veto) === 1 || cell.state === 'off') {
3456
+ html += (html ? ' ' : '') + '<button type="button" class="btn small" data-helper-cell="' + key + '" data-helper-action="enable">Enable</button>';
3457
+ }
3458
+ html += (html ? ' ' : '') + '<button type="button" class="btn small" data-helper-outcomes="' + key + '">Outcomes</button>';
3459
+ return html;
3460
+ }
3461
+
3462
+ // --- WP-6 (§6.9): the per-cell outcome disclosure -------------------------------------------
3463
+ // Every value is escaped; a missing field renders '-'. The five facts are the API's allow-list
3464
+ // (terminal_status, report_source, report_citation_rows, wait_variant, harness) -- no paths, no
3465
+ // brief/report text, never the raw row.
3466
+ function _helperRulesText(value) {
3467
+ return (value == null || value === '') ? '-' : String(value);
3468
+ }
3469
+
3470
+ function _helperRulesOutcomeRows(cellKey) {
3471
+ var state = _helperRulesOutcomes[cellKey];
3472
+ if (!state) return '';
3473
+ var empty = function(message, color) {
3474
+ return '<tr><td colspan="9" style="color:' + (color || 'var(--fg-dim,#565f89)') + ';">' + escHtml(message) + '</td></tr>';
3475
+ };
3476
+ if (state.error) return empty(state.error, '#f7768e');
3477
+ if (!state.rows) return empty('Loading outcomes...');
3478
+ if (!state.rows.length) return empty('No outcomes in this window.');
3479
+ return state.rows.map(function(row) {
3480
+ var facts = row.facts || {};
3481
+ var decision = [row.decision, row.label].filter(function(part) { return part != null && part !== ''; }).join(' / ');
3482
+ return '<tr>' +
3483
+ '<td>' + escHtml(_helperRulesText(row.seq)) + '</td>' +
3484
+ '<td>' + escHtml(decision || '-') + '</td>' +
3485
+ '<td>' + escHtml(_helperRulesText(row.report_rows)) + '</td>' +
3486
+ '<td>' + escHtml(_helperRulesText(facts.terminal_status)) + '</td>' +
3487
+ '<td>' + escHtml(_helperRulesText(facts.report_source)) + '</td>' +
3488
+ '<td>' + escHtml(_helperRulesText(facts.wait_variant)) + '</td>' +
3489
+ '<td>' + escHtml(_helperRulesText(facts.harness)) + '</td>' +
3490
+ '<td>' + escHtml('v' + _helperRulesText(row.label_version)) + '</td>' +
3491
+ '<td>' + escHtml(_helperRulesText(row.created_at)) + '</td>' +
3492
+ '</tr>';
3493
+ }).join('');
3494
+ }
3495
+
3496
+ /** The extra `<tr>` under an opened cell: the cell's trailing outcome window as a nested table. */
3497
+ function _helperRulesOutcomesRow(cellKey) {
3498
+ if (!_helperRulesOutcomes[cellKey]) return '';
3499
+ return '<tr><td colspan="7" style="background:rgba(122,162,247,.06);">' +
3500
+ '<table style="width:100%;border-collapse:collapse;font-size:10px;"><thead><tr>' +
3501
+ '<th style="text-align:left;">Seq</th><th style="text-align:left;">Decision / label</th><th style="text-align:left;">Report rows</th>' +
3502
+ '<th style="text-align:left;">Terminal</th><th style="text-align:left;">Source</th><th style="text-align:left;">Wait</th>' +
3503
+ '<th style="text-align:left;">Harness</th><th style="text-align:left;">Version</th><th style="text-align:left;">Created</th>' +
3504
+ '</tr></thead><tbody>' + _helperRulesOutcomeRows(cellKey) + '</tbody></table></td></tr>';
3505
+ }
3506
+
3507
+ /** Open/close one cell's disclosure, fetching its window once per open. */
3508
+ function _helperRulesToggleOutcomes(cellKey) {
3509
+ if (_helperRulesOutcomes[cellKey]) {
3510
+ delete _helperRulesOutcomes[cellKey];
3511
+ _renderRoutingWorkflows();
3512
+ return null;
3513
+ }
3514
+ _helperRulesOutcomes[cellKey] = { rows: null, error: null };
3515
+ _renderRoutingWorkflows();
3516
+ return fetch('/api/wall-e/helper-rules/cells/' + encodeURIComponent(cellKey) + '/outcomes?limit=20')
3517
+ .then(function(res) { return res.json(); })
3518
+ .then(function(data) {
3519
+ _helperRulesOutcomes[cellKey] = { rows: (data && data.outcomes) || [], error: null };
3520
+ _renderRoutingWorkflows();
3521
+ })
3522
+ .catch(function(err) {
3523
+ _helperRulesOutcomes[cellKey] = { rows: [], error: (err && err.message) || 'Failed to load outcomes' };
3524
+ _renderRoutingWorkflows();
3525
+ });
3526
+ }
3527
+
3528
+ function _routingHelperRulesMarkup() {
3529
+ if (!_helperRulesCells) {
3530
+ return '<div style="font-size:11px;color:var(--fg-dim,#565f89);">' + escHtml(_helperRulesError || 'Loading helper rules...') + '</div>';
3531
+ }
3532
+ var rows = _helperRulesCells.map(function(cell) {
3533
+ return '<tr data-helper-cell="' + escHtml(cell.cell_key) + '"' + (cell.collecting ? ' style="opacity:.58;"' : '') + '>' +
3534
+ '<td style="overflow-wrap:anywhere;">' + escHtml(cell.cell_key) + '</td>' +
3535
+ '<td>' + escHtml(cell.kind || '-') + '</td>' +
3536
+ '<td>' + _helperRulesStateChip(cell) + '</td>' +
3537
+ '<td>' + Number(cell.n_window || 0) + '</td>' +
3538
+ '<td>' + escHtml(_helperRulesRate(cell)) + '</td>' +
3539
+ '<td>' + _helperRulesLastEvent(cell) + '</td>' +
3540
+ '<td>' + _helperRulesActions(cell) + '</td></tr>' +
3541
+ _helperRulesOutcomesRow(cell.cell_key);
3542
+ }).join('');
3543
+ return '<div style="font-size:11px;color:var(--fg-dim,#565f89);margin-bottom:9px;">The two routing rule cells: the wait-watch wake and the exploration hand-off. A veto is final until you enable the cell again.</div>' +
3544
+ '<div style="overflow-x:auto;"><table style="width:100%;border-collapse:collapse;font-size:11px;"><thead><tr><th>Cell</th><th>Kind</th><th>State</th><th>n</th><th>Precision / fallback</th><th>Last event</th><th>Action</th></tr></thead><tbody>' +
3545
+ (rows || '<tr><td colspan="7">No rule cells yet.</td></tr>') + '</tbody></table></div>' +
3546
+ (_helperRulesError ? '<div style="font-size:10px;color:#f7768e;margin-top:7px;">' + escHtml(_helperRulesError) + '</div>' : '');
3547
+ }
3548
+
3549
+ function _loadHelperRules() {
3550
+ _helperRulesError = null;
3551
+ return fetch('/api/wall-e/helper-rules/cells').then(function(res) { return res.json(); }).then(function(data) {
3552
+ _helperRulesCells = (data && data.cells) || [];
3553
+ _renderRoutingWorkflows();
3554
+ }).catch(function(err) {
3555
+ _helperRulesCells = null;
3556
+ _helperRulesError = (err && err.message) || 'Failed to load helper rules';
3557
+ _renderRoutingWorkflows();
3558
+ });
3559
+ }
3560
+
3561
+ function _helperRulesSetState(cellKey, action) {
3562
+ var label = action === 'veto' ? 'Veto' : 'Enable';
3563
+ return _routingConfirm(label + ' helper rule', label + ' ' + cellKey + '?', label).then(function(ok) {
3564
+ if (!ok) { _renderRoutingWorkflows(); return null; } // re-enable the button the click disabled
3565
+ return fetch('/api/wall-e/helper-rules/cells/' + encodeURIComponent(cellKey) + '/' + action, { method: 'POST' })
3566
+ .then(function(res) { return res.json(); })
3567
+ .then(function(data) {
3568
+ if (!data || data.ok === false) throw new Error((data && data.error) || 'request failed');
3569
+ return _loadHelperRules();
3570
+ })
3571
+ .catch(function(err) {
3572
+ _helperRulesError = cellKey + ': ' + ((err && err.message) || 'request failed');
3573
+ _renderRoutingWorkflows();
3574
+ });
3575
+ });
3576
+ }
3577
+
3161
3578
  function _renderRoutingWorkflows() {
3162
3579
  var el = document.getElementById('models-routing-workflows'); if (!el) return;
3163
3580
  var body = _routingWorkflowPill === 'measurements' ? _routingMeasurementsMarkup()
3164
- : (_routingWorkflowView === 'editor' ? _routingWorkflowEditorMarkup(_routingWorkflowSelected()) : _routingWorkflowLibraryMarkup());
3165
- el.innerHTML = DOMPurify.sanitize('<section>' + _routingWorkflowPills() + body + '</section>', { ADD_TAGS: ['article','fieldset','legend'], ADD_ATTR: ['style','data-routing-pill','data-routing-new','data-routing-import','data-routing-edit','data-routing-duplicate','data-routing-export','data-routing-dryrun','data-routing-run','data-routing-back','data-routing-save','data-routing-editor-test','data-node-field','data-node-remove','data-node-add','data-routing-default-policy','data-routing-measure-workflow','data-routing-load-measure','data-routing-refresh-suggestions','data-suggestion-action','data-suggestion-id','data-run-rating','data-run-control','data-run-id','data-run-node','data-run-attempt','data-run-type','data-run-choice','data-policy-fields','data-policy-arm','data-policy-index','data-arm-field','data-arm-saved','data-policy-sample','data-policy-auto','data-policy-cost-lambda','data-policy-latency-budget','data-policy-exploration-budget','data-routing-launch-mode','data-routing-bandit-preview','data-routing-bandit-apply','data-bandit-readiness','data-routing-bandit-readiness-save','aria-label','role','rows','placeholder','value','selected','disabled','checked','min','max','step','type','title'] });
3581
+ : (_routingWorkflowPill === 'helper-rules' ? _routingHelperRulesMarkup()
3582
+ : (_routingWorkflowView === 'editor' ? _routingWorkflowEditorMarkup(_routingWorkflowSelected()) : _routingWorkflowLibraryMarkup()));
3583
+ el.innerHTML = DOMPurify.sanitize('<section>' + _routingWorkflowPills() + body + '</section>', { ADD_TAGS: ['article','fieldset','legend'], ADD_ATTR: ['style','data-routing-pill','data-routing-new','data-routing-import','data-routing-edit','data-routing-duplicate','data-routing-export','data-routing-dryrun','data-routing-run','data-routing-back','data-routing-save','data-routing-editor-test','data-node-field','data-node-remove','data-node-add','data-routing-default-policy','data-routing-measure-workflow','data-routing-load-measure','data-routing-refresh-suggestions','data-suggestion-action','data-suggestion-id','data-run-rating','data-run-control','data-run-id','data-run-node','data-run-attempt','data-run-type','data-run-choice','data-policy-fields','data-policy-arm','data-policy-index','data-arm-field','data-arm-saved','data-policy-sample','data-policy-auto','data-policy-cost-lambda','data-policy-latency-budget','data-policy-exploration-budget','data-routing-launch-mode','data-routing-bandit-preview','data-routing-bandit-apply','data-bandit-readiness','data-routing-bandit-readiness-save','data-helper-cell','data-helper-action','data-helper-outcomes','aria-label','role','rows','placeholder','value','selected','disabled','checked','min','max','step','type','title'] });
3166
3584
  _routingBindArmModelFilters(el);
3167
3585
  var launchAutoOption = el.querySelector('[data-routing-launch-mode] option[value="auto"]');
3168
3586
  if (launchAutoOption) launchAutoOption.disabled = !(_routingLaunchState && (_routingLaunchState.ready || _routingLaunchState.mode === 'auto'));
3169
- el.querySelectorAll('[data-routing-pill]').forEach(function(btn) { btn.onclick = function() { _routingWorkflowPill = btn.dataset.routingPill; if (_routingWorkflowPill === 'policies') { var routes = document.getElementById('models-task-routes'); if (routes) routes.scrollIntoView({ block: 'start' }); } _renderRoutingWorkflows(); }; });
3587
+ el.querySelectorAll('[data-routing-pill]').forEach(function(btn) { btn.onclick = function() { _routingWorkflowPill = btn.dataset.routingPill; if (_routingWorkflowPill === 'policies') { var routes = document.getElementById('models-task-routes'); if (routes) routes.scrollIntoView({ block: 'start' }); } _renderRoutingWorkflows(); if (_routingWorkflowPill === 'helper-rules' && !_helperRulesCells) _loadHelperRules(); }; });
3588
+ el.querySelectorAll('[data-helper-action]').forEach(function(btn) { btn.onclick = function() { var cellKey = btn.dataset.helperCell; var action = btn.dataset.helperAction; btn.disabled = true; _helperRulesSetState(cellKey, action); }; });
3589
+ el.querySelectorAll('[data-helper-outcomes]').forEach(function(btn) { btn.onclick = function() { _helperRulesToggleOutcomes(btn.dataset.helperOutcomes); }; });
3170
3590
  el.querySelectorAll('[data-routing-edit]').forEach(function(btn) { btn.onclick = function() { _routingWorkflowId = btn.dataset.routingEdit; _routingWorkflowView = 'editor'; _renderRoutingWorkflows(); }; });
3171
3591
  var back = el.querySelector('[data-routing-back]'); if (back) back.onclick = function() { _routingWorkflowView = 'library'; _renderRoutingWorkflows(); };
3172
3592
  var add = el.querySelector('[data-routing-new]'); if (add) add.onclick = function() { var id = 'wf_new-' + Date.now(); _routingWorkflows.push({ id:id,name:'New workflow',summary:'Describe when this workflow should run.',category:'coding',version:0,source:'user',enabled:true,autoMatch:true,matchThreshold:.62,defaultPolicy:{type:'inherit'},nodes:[] }); _routingWorkflowId=id; _routingWorkflowView='editor'; _renderRoutingWorkflows(); };
@@ -3473,8 +3893,11 @@ function _delegatedWorkerSkillButtonMarkup(agentId, plan) {
3473
3893
  return '<button type="button" data-install-cli-skill="' + escHtml(agentId) + '"' + extra + ' style="padding:4px 10px;border-radius:5px;border:1px solid var(--border,#414868);background:transparent;color:var(--fg,#c0caf5);cursor:pointer;font-size:11px;">' + escHtml(label) + '</button>';
3474
3894
  }
3475
3895
 
3896
+ // One agent's row, anchored by `data-delegated-worker-skill-row="<agentId>"` so a
3897
+ // reader (or a spec) can scope to THIS row instead of a bare text match that any
3898
+ // unrelated "unavailable"/version string elsewhere in the section also satisfies.
3476
3899
  function _delegatedWorkerSkillRowMarkup(agentId, agentName, plan) {
3477
- return '<div style="display:flex;justify-content:space-between;gap:10px;align-items:center;flex-wrap:wrap;padding:3px 0;">' +
3900
+ return '<div data-delegated-worker-skill-row="' + escHtml(agentId) + '" style="display:flex;justify-content:space-between;gap:10px;align-items:center;flex-wrap:wrap;padding:3px 0;">' +
3478
3901
  '<div style="font-size:11px;color:var(--fg-dim,#565f89);">delegate · ' + escHtml(agentName) + '</div>' +
3479
3902
  '<div style="display:flex;gap:8px;align-items:center;">' +
3480
3903
  '<span style="font-size:10px;color:var(--fg-dim,#565f89);">' + escHtml(_delegatedWorkerSkillPlanLabel(plan)) + '</span>' +
@@ -4281,6 +4704,54 @@ function _codingAgentUpgradeRenderJobDialog(job) {
4281
4704
  };
4282
4705
  }
4283
4706
 
4707
+ // WP-2 fix (B): the card is rebuilt with `el.innerHTML = …`, so a legitimate repaint
4708
+ // (flow moved on, account changed) also throws away whatever the user had typed into
4709
+ // the sign-in code box, its focus and its caret. Capture the drafts before the swap
4710
+ // and put them back on the matching card after the bindings are attached. Pure DOM,
4711
+ // no timers: a card whose new markup has no code box (the flow closed) drops its draft.
4712
+ function _modelsCodingAgentCaptureSignInDrafts(el) {
4713
+ var drafts = [];
4714
+ if (!el || typeof el.querySelectorAll !== 'function') return drafts;
4715
+ var doc = el.ownerDocument || (typeof document !== 'undefined' ? document : null);
4716
+ el.querySelectorAll('[data-coding-agent-card]').forEach(function(card) {
4717
+ var code = typeof card.querySelector === 'function' ? card.querySelector('[data-sign-in-code]') : null;
4718
+ if (!code) return;
4719
+ var error = typeof card.querySelector === 'function' ? card.querySelector('[data-sign-in-error]') : null;
4720
+ drafts.push({
4721
+ cardId: (card.dataset && card.dataset.codingAgentCard) || '',
4722
+ value: code.value,
4723
+ selectionStart: typeof code.selectionStart === 'number' ? code.selectionStart : null,
4724
+ selectionEnd: typeof code.selectionEnd === 'number' ? code.selectionEnd : null,
4725
+ focused: !!(doc && doc.activeElement && doc.activeElement === code),
4726
+ error: error ? error.textContent : null,
4727
+ });
4728
+ });
4729
+ return drafts;
4730
+ }
4731
+
4732
+ function _modelsCodingAgentRestoreSignInDrafts(el, drafts) {
4733
+ if (!el || !drafts || !drafts.length) return;
4734
+ if (typeof el.querySelectorAll !== 'function') return;
4735
+ var byCard = {};
4736
+ drafts.forEach(function(draft) { byCard[draft.cardId] = draft; });
4737
+ el.querySelectorAll('[data-coding-agent-card]').forEach(function(card) {
4738
+ var draft = byCard[(card.dataset && card.dataset.codingAgentCard) || ''];
4739
+ if (!draft) return;
4740
+ var code = typeof card.querySelector === 'function' ? card.querySelector('[data-sign-in-code]') : null;
4741
+ if (!code) return;
4742
+ if (typeof draft.value === 'string') code.value = draft.value;
4743
+ if (typeof draft.error === 'string') {
4744
+ var error = typeof card.querySelector === 'function' ? card.querySelector('[data-sign-in-error]') : null;
4745
+ if (error) error.textContent = draft.error;
4746
+ }
4747
+ if (!draft.focused) return;
4748
+ if (typeof code.focus === 'function') code.focus();
4749
+ if (typeof code.setSelectionRange === 'function' && draft.selectionStart !== null && draft.selectionEnd !== null) {
4750
+ code.setSelectionRange(draft.selectionStart, draft.selectionEnd);
4751
+ }
4752
+ });
4753
+ }
4754
+
4284
4755
  function renderModelsCodingAgentRoutes(providers, models) {
4285
4756
  var el = document.getElementById('models-coding-agents-section');
4286
4757
  if (!el) return;
@@ -4392,7 +4863,7 @@ function renderModelsCodingAgentRoutes(providers, models) {
4392
4863
  _modelsCodingAgentMeta('CLI', cliLabel, status.cliReady ? '#9ece6a' : '#e0af68') +
4393
4864
  _modelsCodingAgentOtherInstallsHtml(versionInfo) +
4394
4865
  _modelsCodingAgentMeta('Version', upgradeRunning ? _codingAgentUpgradePhaseText(upgradeJob) : _modelsCodingAgentVersionText(versionInfo), upgradeRunning ? '#7aa2f7' : ((versionInfo && versionInfo.hasUpdate) ? '#e0af68' : 'var(--fg,#c0caf5)'), upgradeRunning ? null : { title: _modelsCodingAgentVersionTitle(versionInfo), dim: _modelsCodingAgentVersionDim(versionInfo) }) +
4395
- (_modelsProviderIdForPreset(preset) ? _modelsCodingAgentMeta('Account', _modelsCodingAgentAccountText(preset), _modelsCodingAgentAccountColor(preset)) : '') +
4866
+ (_modelsProviderIdForPreset(preset) ? _modelsCodingAgentMeta('Account', _modelsCodingAgentAccountText(preset), _modelsCodingAgentAccountColor(preset), { title: _modelsCodingAgentAccountTitle(preset) }) : '') +
4396
4867
  _modelsCodingAgentMeta('CTM Status', hookLabel, (status.hooksSupported === false) ? (status.cliReady ? '#9ece6a' : 'var(--fg-dim,#565f89)') : (status.hookReady ? '#9ece6a' : '#e0af68')) +
4397
4868
  ((preset.id === 'codex' || preset.id === 'claude-code') && status.hooksSupported !== false ? _modelsCodingAgentMeta('OTLP telemetry', _modelsOtelReceiptText((((_modelsAgentStatus || {}).hooks || {}).agents || {})[preset.id]), 'var(--fg-dim,#565f89)') : '') +
4398
4869
  _modelsCodingAgentMeta('Model Source', preset.modelSource) +
@@ -4435,9 +4906,12 @@ function renderModelsCodingAgentRoutes(providers, models) {
4435
4906
  '</div>' +
4436
4907
  _delegatedWorkersGroupMarkup(providers, models, _delegatedWorkerStatus) +
4437
4908
  '</section>';
4909
+ // WP-2 fix (B): whatever the user had typed into a sign-in code box survives this
4910
+ // rebuild — captured here, put back once the new markup is bound below.
4911
+ var signInDrafts = _modelsCodingAgentCaptureSignInDrafts(el);
4438
4912
  el.innerHTML = DOMPurify.sanitize(h, {
4439
4913
  ADD_TAGS: ['article', 'code', 'select', 'option'],
4440
- ADD_ATTR: ['style', 'data-coding-agent-card', 'data-agent-state', 'data-agent-update', 'data-agent-update-banner', 'data-agent-upgrade-job', 'data-install-agent-hooks', 'data-install-missing-agent-hooks', 'data-install-outdated-agent-hooks', 'data-live-status-update-banner', 'data-live-status-missing-banner', 'data-refresh-agent-status', 'data-upgrade-agent', 'data-agent-auto-update-toggle', 'data-agent-other-installs', 'data-agent-auto-attempt', 'checked', 'data-upgrade-all-agents', 'data-view-upgrade-job', 'data-view-upgrade-batch', 'data-review-agent-updates', 'data-dismiss-agent-updates', 'data-delegated-worker-route', 'data-delegated-worker-route-filter', 'data-delegated-worker-effective', 'placeholder', 'aria-label', 'autocomplete', 'data-delegated-workers-group', 'data-install-cli-skill', 'data-install-cli-overwrite', 'type', 'disabled', 'title', 'id', 'for', 'value', 'selected', 'data-sign-in-agent', 'data-show-sign-in-tab', 'data-open-sign-in-page', 'data-sign-in-hint', 'data-sign-in-failed', 'href', 'target', 'rel'],
4914
+ ADD_ATTR: ['style', 'data-coding-agent-card', 'data-agent-state', 'data-agent-update', 'data-agent-update-banner', 'data-agent-upgrade-job', 'data-install-agent-hooks', 'data-install-missing-agent-hooks', 'data-install-outdated-agent-hooks', 'data-live-status-update-banner', 'data-live-status-missing-banner', 'data-refresh-agent-status', 'data-upgrade-agent', 'data-agent-auto-update-toggle', 'data-agent-other-installs', 'data-agent-auto-attempt', 'checked', 'data-upgrade-all-agents', 'data-view-upgrade-job', 'data-view-upgrade-batch', 'data-review-agent-updates', 'data-dismiss-agent-updates', 'data-delegated-worker-route', 'data-delegated-worker-route-filter', 'data-delegated-worker-effective', 'placeholder', 'aria-label', 'autocomplete', 'data-delegated-workers-group', 'data-delegated-worker-skill-row', 'data-install-cli-skill', 'data-install-cli-overwrite', 'type', 'disabled', 'title', 'id', 'for', 'value', 'selected', 'data-sign-in-agent', 'data-show-sign-in-tab', 'data-open-sign-in-page', 'data-sign-in-hint', 'data-sign-in-failed', 'data-sign-in-user-code', 'data-sign-in-copy-code', 'data-sign-in-code', 'data-sign-in-code-submit', 'data-sign-in-error', 'inputmode', 'spellcheck', 'href', 'target', 'rel'],
4441
4915
  });
4442
4916
  el.querySelectorAll('[data-delegated-worker-route]').forEach(function(select) {
4443
4917
  var filter = select.parentNode.querySelector('[data-delegated-worker-route-filter]');
@@ -4623,6 +5097,42 @@ function renderModelsCodingAgentRoutes(providers, models) {
4623
5097
  try { activateTab(sessionId); } catch (e) { /* expected: an already-closed tab cannot be activated */ }
4624
5098
  });
4625
5099
  });
5100
+ // The code entry (WP-2): the submit button and Enter in the box both POST the
5101
+ // pasted value to the flow's provider, resolved from the card the markup sits in.
5102
+ el.querySelectorAll('[data-sign-in-code-submit], [data-sign-in-code]').forEach(function(node) {
5103
+ var submit = function() {
5104
+ var card = node.closest('[data-coding-agent-card]');
5105
+ var preset = card ? _modelsCodingAgentPresetById(card.dataset.codingAgentCard) : null;
5106
+ var providerId = preset ? _modelsProviderIdForPreset(preset) : '';
5107
+ var submitBtn = node.hasAttribute('data-sign-in-code-submit')
5108
+ ? node
5109
+ : node.parentNode.querySelector('[data-sign-in-code-submit]');
5110
+ _modelsProviderLoginSubmitCode(
5111
+ providerId,
5112
+ node.hasAttribute('data-sign-in-code') ? node : node.parentNode.querySelector('[data-sign-in-code]'),
5113
+ node.parentNode.querySelector('[data-sign-in-error]'),
5114
+ submitBtn,
5115
+ );
5116
+ };
5117
+ if (node.hasAttribute('data-sign-in-code-submit')) {
5118
+ node.addEventListener('click', submit);
5119
+ return;
5120
+ }
5121
+ node.addEventListener('keydown', function(ev) {
5122
+ if (ev.key !== 'Enter') return;
5123
+ ev.preventDefault();
5124
+ submit();
5125
+ });
5126
+ });
5127
+ el.querySelectorAll('[data-sign-in-copy-code]').forEach(function(btn) {
5128
+ btn.addEventListener('click', function() {
5129
+ var codeEl = btn.parentNode.querySelector('[data-sign-in-user-code]');
5130
+ if (codeEl) _modelsProviderLoginCopyCode(codeEl.textContent, btn);
5131
+ });
5132
+ });
5133
+ // WP-2 fix (B): the bindings are attached — a draft the user was typing survives the
5134
+ // rebuild above, focus and caret included.
5135
+ _modelsCodingAgentRestoreSignInDrafts(el, signInDrafts);
4626
5136
  // Resumes the 2 s poll after a re-render while a flow is still open.
4627
5137
  _modelsProviderLoginEnsurePoll();
4628
5138
  if (!_codingAgentUpgradeHasRunningJob()) _maybePromptCodingAgentHookSetupFromAgentsTab();