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
@@ -0,0 +1,203 @@
1
+ 'use strict';
2
+
3
+ // A1 (override-preserving dispatch observation): the ONE leaf every hosted input frame
4
+ // travels through when it is put on a wire. These helpers DO perform that existing wire leaf --
5
+ // they are the real dispatch, not a bystander -- but they add NO independent or parallel route, no
6
+ // override bypass, no no-post witness and no durable/protocol authority. The frozen
7
+ // {posted, disposition} result is DATA: an uncertain route is never reported as proof, and a
8
+ // caller's legacy Boolean is never upgraded by a guess.
9
+ //
10
+ // Scope model -- two PRIVATE WeakMaps, read and written only here:
11
+ // _adapterScopes adapter -> the collector for ONE observeAdapterWrite invocation
12
+ // _messageScopes message -> the collector of the invocation that built that frame
13
+ // The message binding is what carries one observation across the EXISTING routing seams -- a
14
+ // selected `write` override, a selected `_post` override, the manager's pooled COPY of the
15
+ // frame, and a selected `_postToPool` override -- so no parallel dispatch path is added and
16
+ // an uninstrumented override still wins (it simply leaves the invocation uncertain).
17
+ //
18
+ // Requires ONLY the encoder on purpose: the session-host manager requires this module, so
19
+ // requiring it back would be a cycle.
20
+ const { encodeFrame } = require('./host-control-protocol');
21
+
22
+ // Fixed vocabulary, ordered by join rank. A later refusal must not erase an endpoint that was
23
+ // entered, and a later success must not launder an earlier uncertainty of the SAME invocation;
24
+ // a genuinely separate successful dispatch is simply observed as its own asserted result.
25
+ const PREFLIGHT_REFUSED = 'preflight-refused';
26
+ const DISPATCH_ASSERTED = 'dispatch-asserted';
27
+ const DISPATCH_UNCERTAIN = 'dispatch-uncertain';
28
+ const JOIN_RANK = new Map([[PREFLIGHT_REFUSED, 1], [DISPATCH_ASSERTED, 2], [DISPATCH_UNCERTAIN, 3]]);
29
+
30
+ const _adapterScopes = new WeakMap();
31
+ const _messageScopes = new WeakMap();
32
+
33
+ function _observation(posted, disposition) {
34
+ return Object.freeze({ posted: posted === true, disposition });
35
+ }
36
+
37
+ function _createCollector() {
38
+ let disposition = '';
39
+ return {
40
+ record(next) {
41
+ if ((JOIN_RANK.get(next) || 0) > (JOIN_RANK.get(disposition) || 0)) disposition = next;
42
+ },
43
+ disposition() { return disposition; },
44
+ };
45
+ }
46
+
47
+ function _collectorFor(message) {
48
+ return message && typeof message === 'object' ? _messageScopes.get(message) || null : null;
49
+ }
50
+
51
+ // Record one leaf outcome in the exact frame collector, if this frame is inside an observed
52
+ // invocation at all. Outside one, this is a no-op: observation never creates state of its own.
53
+ function _record(message, disposition) {
54
+ const collector = _collectorFor(message);
55
+ if (collector) collector.record(disposition);
56
+ }
57
+
58
+ // A refusal is a fact about THIS call taking no transport action; it is recorded so a later
59
+ // successful dispatch of the same invocation can still be joined above it. `posted` is explicit
60
+ // per leaf: only the SELECTED SOCKET inner refusal keeps the legacy `true` (the pre-observation
61
+ // owner returned true even when its inner write did nothing); an absent/disconnected IPC child is
62
+ // `false`, exactly as the legacy `return false` it replaces.
63
+ function _refuse(message, posted) {
64
+ _record(message, PREFLIGHT_REFUSED);
65
+ return _observation(posted, PREFLIGHT_REFUSED);
66
+ }
67
+
68
+ // Note that an EXPLICIT no-transport preflight branch refused to dispatch this frame at all
69
+ // (missing/disconnected transport, closed adapter/pool, absent manager). Call it only from such a
70
+ // preflighted refusal branch -- never from an arbitrary branch after a dispatch was attempted,
71
+ // where the honest record is uncertainty, not a refusal that would understate what happened.
72
+ function notePostRefusal(message) {
73
+ _record(message, PREFLIGHT_REFUSED);
74
+ }
75
+
76
+ // The ONE socket leaf. The legacy boundary swallowed an encoding/write failure and reported the
77
+ // frame as posted, so that result is preserved -- the failure is recorded as uncertainty instead
78
+ // (never a new host-error, never fabricated as asserted). The caller owns the `destroyed`
79
+ // preflight it has always owned; a connection that dies between the two checks refuses here.
80
+ function postSocketFrameObserved(conn, message) {
81
+ if (!conn || conn.destroyed) return _refuse(message, true);
82
+ let outcome = DISPATCH_ASSERTED;
83
+ try {
84
+ conn.write(encodeFrame(message));
85
+ } catch {
86
+ outcome = DISPATCH_UNCERTAIN; // expected: the connection may already be gone
87
+ }
88
+ _record(message, outcome);
89
+ return _observation(true, outcome);
90
+ }
91
+
92
+ // The ONE IPC leaf. A normal return -- including the false a full send backlog produces -- is a
93
+ // completed dispatch of this frame and is asserted; only a thrown send is uncertain, and it is
94
+ // recorded BEFORE the original error callback runs, so a throwing callback cannot lose it.
95
+ function postIpcFrameObserved(child, message, onError) {
96
+ if (!child || !child.connected) return _refuse(message, false);
97
+ try {
98
+ child.send(message);
99
+ } catch (err) {
100
+ _record(message, DISPATCH_UNCERTAIN);
101
+ if (typeof onError === 'function') onError(err);
102
+ return _observation(false, DISPATCH_UNCERTAIN);
103
+ }
104
+ _record(message, DISPATCH_ASSERTED);
105
+ return _observation(true, DISPATCH_ASSERTED);
106
+ }
107
+
108
+ // The WHOLE selected-socket boundary, extracted rather than re-implemented. The caller keeps its
109
+ // own selection check exactly where it was (that `destroyed` read is lookup #1); the connection
110
+ // thunk is evaluated INSIDE this try, so lookup #2 happens in the leaf. This catch is for failures
111
+ // that ESCAPE the inner helper -- the connection lookup/preflight itself -- which become one
112
+ // uncertainty record on the exact frame, the original error callback, and the legacy `false`
113
+ // only if that callback returns. An encoding or write exception is NOT an outer failure: the
114
+ // inner helper swallows it and returns `true`/`dispatch-uncertain` with no host-error, so this
115
+ // boundary never converts it into a `false`. A throwing callback propagates; it is never swallowed.
116
+ // The `true` return is the legacy result of a selected socket dispatch that completed, or of a leaf
117
+ // refusal that never touched the wire.
118
+ function postSelectedSocket(getConnection, message, onError) {
119
+ try {
120
+ postSocketFrameObserved(getConnection(), message);
121
+ return true;
122
+ } catch (error) {
123
+ _record(message, DISPATCH_UNCERTAIN);
124
+ if (typeof onError === 'function') onError(error);
125
+ return false;
126
+ }
127
+ }
128
+
129
+ // Run the ACTUAL selected `_post` for a frame built by the selected `write`, associating the
130
+ // frame with this invocation's collector while it runs. The raw callback result is returned
131
+ // unchanged so the legacy `=== true` Boolean keeps its exact meaning.
132
+ function withAdapterPostObservation(adapter, message, invoke) {
133
+ const collector = _adapterScopes.get(adapter);
134
+ if (!collector || !message || typeof message !== 'object') return invoke();
135
+ const previous = _messageScopes.get(message);
136
+ _messageScopes.set(message, collector);
137
+ try {
138
+ return invoke();
139
+ } finally {
140
+ if (previous === undefined) _messageScopes.delete(message);
141
+ else _messageScopes.set(message, previous);
142
+ }
143
+ }
144
+
145
+ // Carry the source frame's collector onto the COPY the selected `_post` hands to `_postToPool`,
146
+ // for the synchronous actual `_postToPool` callback only. The previous binding is restored even
147
+ // when that callback throws, so a nested forwarded scope cannot leak into a later dispatch.
148
+ function forwardPostObservation(sourceMessage, forwardedMessage, invoke) {
149
+ const collector = _collectorFor(sourceMessage);
150
+ if (!collector || !forwardedMessage || typeof forwardedMessage !== 'object') return invoke();
151
+ const previous = _messageScopes.get(forwardedMessage);
152
+ _messageScopes.set(forwardedMessage, collector);
153
+ try {
154
+ return invoke();
155
+ } finally {
156
+ if (previous === undefined) _messageScopes.delete(forwardedMessage);
157
+ else _messageScopes.set(forwardedMessage, previous);
158
+ }
159
+ }
160
+
161
+ // Observe ONE actual dispatch through the ACTUAL selected writer -- `write` here is whatever the
162
+ // caller selected, never a prototype bypass. Nested/reentrant calls see their own scope restored
163
+ // on the way out, and a throw from the selected writer propagates with every binding cleaned.
164
+ function observeAdapterWrite(adapter, invoke) {
165
+ const previous = _adapterScopes.get(adapter);
166
+ const collector = _createCollector();
167
+ _adapterScopes.set(adapter, collector);
168
+ try {
169
+ const result = invoke();
170
+ return _observation(result !== false, collector.disposition() || DISPATCH_UNCERTAIN);
171
+ } finally {
172
+ if (previous === undefined) _adapterScopes.delete(adapter);
173
+ else _adapterScopes.set(adapter, previous);
174
+ }
175
+ }
176
+
177
+ // Observe a write to a PTY-like object. An instrumented handle reports its own real route; a
178
+ // malformed report is NEVER retried and never trusted -- it is frozen false/uncertain, because
179
+ // this leaf has no transport of its own to check. An opaque handle keeps its exact legacy result
180
+ // (`!== false`) and is ALWAYS uncertain: a normal return proves only that write() returned. An
181
+ // invalid/unavailable opaque writer is NOT swallowed into a fabricated refusal -- the original
182
+ // TypeError propagates, because the caller already owns its own missing-PTY preflight.
183
+ function observePtyWrite(pty, data, meta) {
184
+ if (typeof pty?.writeObserved === 'function') {
185
+ const observed = pty.writeObserved(data, meta);
186
+ if (observed && typeof observed.posted === 'boolean' && JOIN_RANK.has(observed.disposition)) {
187
+ return _observation(observed.posted, observed.disposition);
188
+ }
189
+ return _observation(false, DISPATCH_UNCERTAIN);
190
+ }
191
+ return _observation(pty.write(data, meta) !== false, DISPATCH_UNCERTAIN);
192
+ }
193
+
194
+ module.exports = {
195
+ postSocketFrameObserved,
196
+ postIpcFrameObserved,
197
+ postSelectedSocket,
198
+ notePostRefusal,
199
+ withAdapterPostObservation,
200
+ forwardPostObservation,
201
+ observeAdapterWrite,
202
+ observePtyWrite,
203
+ };
@@ -3,6 +3,9 @@
3
3
  const crypto = require('crypto');
4
4
  const { dispatchOwnerMutation } = require('./owner-mutation-disposition');
5
5
  const { MAX_BATCH_BYTES, jsonByteLength, batchEntriesByBytes } = require('./sync-push');
6
+ // WP-0 (remote-machine-hub-sessions-2026-10-22.html D-4): the machine registry's liveness probe
7
+ // rides THIS tick rather than owning a timer, so the hub keeps exactly one outbound cadence.
8
+ const machineRegistry = require('./machine-registry');
6
9
 
7
10
  const DEFAULT_INTERVAL_MS = 15000;
8
11
  const DEFAULT_GAP_BACKOFF_MS = 5 * 60 * 1000;
@@ -32,6 +35,24 @@ function _authHeaders(env) {
32
35
  };
33
36
  }
34
37
 
38
+ // The machine probe's credential (lib/machine-registry.js): the same headers as a sync leg, or
39
+ // none when this host has no peer token configured (a same-host box answers over loopback).
40
+ //
41
+ // S-12 / P-8: this is only the FALLBACK now. A pool member must dial a pool member with the shared
42
+ // pool secret and a legacy env-token box with the env bearer, and that split needs the target's
43
+ // instance id — which is why the caller injects `peerHeadersFor(instanceId)`. It is injected and not
44
+ // required here because lib/remote-machine-create.js (which owns buildPeerControlHeaders) requires
45
+ // THIS module for parseSyncPeers: requiring it back would close a lib/** cycle (dependency-lint
46
+ // ceiling 0).
47
+ function _probeHeaders(env = process.env) {
48
+ try {
49
+ return _authHeaders(env);
50
+ } catch {
51
+ // expected: no peer token configured — the probe goes out without a credential.
52
+ return {};
53
+ }
54
+ }
55
+
35
56
  function _parsePeerTokenMap(raw = '') {
36
57
  const value = _string(raw, 10000);
37
58
  const map = new Map();
@@ -695,15 +716,91 @@ function _failureDelayMs(env, options, intervalMs, attempt) {
695
716
  return Math.min(max, exp + jitter);
696
717
  }
697
718
 
719
+ // WP-0 (remote-machine-hub-sessions-2026-10-22.html §8): the machine-registry sweep, plus the WP-6
720
+ // live-catalog refresh riding it. Shared by the sync tick and the machines-only loop below; a
721
+ // probe failure is reported, never thrown, and never folded into a sync verdict.
722
+ async function _sweepMachines({ db, fetchImpl, env, peerHeadersFor, selfInstanceId }) {
723
+ try {
724
+ const machineProbe = await machineRegistry.probeMachinesOnce({ fetchImpl }, {
725
+ db,
726
+ // P-8: the per-target credential the server injects (pool secret vs env bearer). The fixed
727
+ // env-token object stays as the fallback for a caller that has not been wired yet.
728
+ ...(typeof peerHeadersFor === 'function' ? { peerHeadersFor } : { peerHeaders: _probeHeaders(env) }),
729
+ // P-5: this machine's own id, so the roster pull never writes its own row. Injected by
730
+ // server.js (which owns the identity accessor) — requiring it here would close a lib/** cycle.
731
+ ...(selfInstanceId ? { selfInstanceId } : {}),
732
+ });
733
+ // WP-6 (AC-6a): deliberately NOT awaited — the catalog talks to the boxes' WS links (a dial
734
+ // plus a reply), and the tick's cadence must not be held hostage by a slow box. A machine the
735
+ // sweep judged unreachable keeps its previous catalog, which degrades its rows to the
736
+ // sync-mirror look.
737
+ try {
738
+ require('./remote-machine-catalog').refreshAll({ db }).catch(() => { /* expected: refreshAll resolves its own per-machine failures */ });
739
+ } catch { /* expected: a catalog fault must never fail a machine probe sweep */ }
740
+ return machineProbe;
741
+ } catch (probeErr) {
742
+ // REPORT: surfaced as status().machines / lastResult.machineProbe; the next tick retries.
743
+ return { ok: false, error: probeErr.message || String(probeErr) };
744
+ }
745
+ }
746
+
747
+ // A hub may register machines (Settings -> Machines) without configuring any sync peer: the box
748
+ // authorises it through the peer TOKEN map alone. Sync stays off (`started:false`, the status
749
+ // surfaces read that as "sync off"), but the registered machines are still probed on the same
750
+ // cadence — otherwise a machine's health would freeze at whatever registration recorded and a
751
+ // stopped box would read "reachable" forever. Zero registered machines => zero fetches (AC-0d).
752
+ function _startMachinesOnlyLoop({ db, fetchImpl, intervalMs, env, peerHeadersFor, selfInstanceId }) {
753
+ let running = false;
754
+ let stopped = false;
755
+ let machineProbe = null;
756
+ const tick = async () => {
757
+ if (running || stopped) return;
758
+ running = true;
759
+ try {
760
+ machineProbe = await _sweepMachines({ db, fetchImpl, env, peerHeadersFor, selfInstanceId });
761
+ } finally {
762
+ running = false;
763
+ }
764
+ };
765
+ const timer = setInterval(tick, intervalMs);
766
+ if (typeof timer.unref === 'function') timer.unref();
767
+ tick();
768
+ return {
769
+ started: false,
770
+ reason: 'no_peers',
771
+ peers: [],
772
+ intervalMs,
773
+ stop() {
774
+ stopped = true;
775
+ clearInterval(timer);
776
+ },
777
+ status() {
778
+ return { started: false, reason: 'no_peers', intervalMs, machines: machineProbe };
779
+ },
780
+ };
781
+ }
782
+
698
783
  function startHostedSyncWorker(options = {}) {
699
784
  const env = options.env || process.env;
700
- // R-12: "peers configured" is the gate (there is no separate enable flag any more).
701
- if (!syncEnabledByConfig(env)) return { started: false, reason: 'no_peers' };
702
- const peers = options.peers || parseSyncPeers(env);
703
- if (!peers.length) return { started: false, reason: 'no_peers' };
704
785
  const db = options.db;
705
786
  const fetchImpl = options.fetch || global.fetch;
706
787
  const intervalMs = Math.max(1000, Number(env.CTM_SYNC_POLL_INTERVAL_MS || options.intervalMs || DEFAULT_INTERVAL_MS) || DEFAULT_INTERVAL_MS);
788
+ // R-12: "peers configured" is the gate (there is no separate enable flag any more).
789
+ if (!syncEnabledByConfig(env)) return _startMachinesOnlyLoop({ db, fetchImpl, intervalMs, env, peerHeadersFor: options.peerHeadersFor, selfInstanceId: options.selfInstanceId });
790
+ const peers = options.peers || parseSyncPeers(env);
791
+ if (!peers.length) return _startMachinesOnlyLoop({ db, fetchImpl, intervalMs, env, peerHeadersFor: options.peerHeadersFor, selfInstanceId: options.selfInstanceId });
792
+ // WP-0 (§8 "seed-from-CTM_SYNC_PEERS"): this worker starts exactly once per boot, which makes
793
+ // it the hub's seeding hook. Every configured peer that is not yet in the registry becomes a
794
+ // DISABLED machines row — env is a seed, never an authority, so nothing is auto-enabled or
795
+ // auto-probed (D-11: no flag, no auto-enable). Idempotent: a restart re-seeds nothing.
796
+ let machineSeed = null;
797
+ try {
798
+ machineSeed = machineRegistry.seedMachinesFromSyncPeers({ db, env, peers });
799
+ } catch (seedErr) {
800
+ // REPORT: surfaced as status().machineSeed; an unseeded machine is a missing card, not a
801
+ // failed boot, and the next boot retries.
802
+ machineSeed = { seeded: 0, skipped: 0, error: seedErr.message || String(seedErr) };
803
+ }
707
804
  let running = false;
708
805
  let stopped = false;
709
806
  let lastResult = null;
@@ -725,6 +822,9 @@ function startHostedSyncWorker(options = {}) {
725
822
  };
726
823
  // Follow-up #17: the last local outbox prune (null until one is attempted).
727
824
  let brainPrune = null;
825
+ // WP-0: the last machine-registry sweep (null until one is attempted). Reported, never folded
826
+ // into the sync verdict.
827
+ let machineProbe = null;
728
828
  const tick = async () => {
729
829
  if (running || stopped) return;
730
830
  running = true;
@@ -818,7 +918,13 @@ function startHostedSyncWorker(options = {}) {
818
918
  }
819
919
  brainPrune = { ...pruneReport, lastAttemptAt: new Date().toISOString() };
820
920
  }
821
- lastResult = { ok: results.every((r) => r.ok !== false), checkedAt: new Date().toISOString(), results, brainPrune: pruneReport };
921
+ // WP-0 (remote-machine-hub-sessions-2026-10-22.html §8): the machine-registry sweep rides
922
+ // this tick, AFTER the peer legs and independent of them — it shares the cadence (D-4) but
923
+ // not the verdict, so a probe failure never marks a sync leg failed. Zero registered
924
+ // machines ⇒ zero fetches (AC-0d), so an instance with no machines performs no extra
925
+ // network work per tick.
926
+ machineProbe = await _sweepMachines({ db, fetchImpl, env, peerHeadersFor: options.peerHeadersFor, selfInstanceId: options.selfInstanceId });
927
+ lastResult = { ok: results.every((r) => r.ok !== false), checkedAt: new Date().toISOString(), results, brainPrune: pruneReport, machineProbe };
822
928
  } catch (err) {
823
929
  lastResult = { ok: false, checkedAt: new Date().toISOString(), error: err.message || String(err) };
824
930
  } finally {
@@ -846,6 +952,8 @@ function startHostedSyncWorker(options = {}) {
846
952
  push: [...pushState.values()],
847
953
  brain: [...brainState.values()],
848
954
  brainPrune,
955
+ machines: machineProbe,
956
+ machineSeed,
849
957
  };
850
958
  },
851
959
  };
@@ -120,6 +120,10 @@ const INSTANCE_ID_COLUMN_MANIFEST = [
120
120
  },
121
121
 
122
122
  // --- live-peer: peer-sync owned, deliberately out of scope for local restamping ---
123
+ {
124
+ table: 'machines', column: 'instance_id', classification: 'live-peer',
125
+ note: 'WP-0 remote-machine-hub (docs/remote-machine-hub-sessions-2026-10-22.html D-3): the REMOTE box\'s minted identity, pinned from its GET /api/sync/manifest at registration. It names another machine, never this one — a local re-mint changes nothing about which box the row points at, so restamping it would corrupt the pin.',
126
+ },
123
127
  {
124
128
  table: 'sync_outbox', column: 'source_instance_id', classification: 'live-peer',
125
129
  note: 'Peer sync bookkeeping. Restamping locally could trigger re-delivery storms on the peer side. Deferred per S-14/S-15.',
@@ -68,6 +68,9 @@ function buildServerInstanceManifest(options = {}) {
68
68
  backupStatus,
69
69
  port: options.port,
70
70
  wallePort: options.wallePort,
71
+ // WP-2a D-4: the machine-owned settings cache (lib/machine-self.js) rides through to the pure
72
+ // builder, which reads it and never the DB. `options.machineSelf` is `current()` from server.js.
73
+ machineSelf: options.machineSelf,
71
74
  pid: options.pid,
72
75
  uptimeSeconds: options.uptimeSeconds,
73
76
  walleStatus: options.walleStatus,
@@ -102,7 +102,13 @@ function buildInstanceManifest(options = {}) {
102
102
  EXECUTION_HOST_KINDS,
103
103
  'local'
104
104
  );
105
- const displayName = _limitedString(
105
+ // WP-2a (machine-pool-picker-pairing-dashboard-2026-10-24.html §5 D-4 rev 2, §9 S-25): the
106
+ // machine-owned settings, passed IN through `options.machineSelf` (lib/machine-self.js's cache) so
107
+ // the builder stays pure and never reads the DB. A non-empty displayName overrides the env/default
108
+ // name; favorite/hidden ride the manifest as booleans so every primary reads the same values.
109
+ const machineSelf = options.machineSelf && typeof options.machineSelf === 'object' ? options.machineSelf : null;
110
+ const selfDisplayName = _limitedString(machineSelf && machineSelf.displayName, '');
111
+ const displayName = selfDisplayName || _limitedString(
106
112
  env.CTM_EXECUTION_HOST_DISPLAY_NAME || env.CTM_INSTANCE_DISPLAY_NAME,
107
113
  executionHostKind === 'hosted' ? hostname : 'Local CTM'
108
114
  );
@@ -158,8 +164,14 @@ function buildInstanceManifest(options = {}) {
158
164
  executionHost: {
159
165
  kind: executionHostKind,
160
166
  displayName,
167
+ // D-4 rev 2: machine-owned booleans. `favorite` is the picker star; `hidden` is "hide from
168
+ // picker". NEVER `pinned` — that word already means the identity pin (lib/machine-registry.js).
169
+ favorite: !!(machineSelf && machineSelf.favorite),
170
+ hidden: !!(machineSelf && machineSelf.hidden),
161
171
  hostname,
162
172
  os: _platform(options, env),
173
+ // Machine pool D-2/D-10: os/arch name the box in the picker search and the Machines card.
174
+ arch: _limitedString(options.arch || process.arch || '', ''),
163
175
  hostLabel: _limitedString(env.CTM_SERVER_HOST_LABEL || env.CTM_HOST_LABEL, ''),
164
176
  bindHost: host,
165
177
  publicOrigin: _limitedString(
@@ -76,6 +76,8 @@ function buildInstanceSyncStatus(options = {}) {
76
76
  const hasDurableSyncSchema = _phaseEnabledBySchema(hybridEnabled, hasOwnership, hasOutbox);
77
77
  const outboxStats = schema.outbox || {};
78
78
  const worker = schema.worker || {};
79
+ // S-30: supplied by the caller (see the `machines` key in the return below).
80
+ const machinesInput = options.machines && typeof options.machines === 'object' ? options.machines : {};
79
81
 
80
82
  const warnings = [];
81
83
  if (mode !== 'none' && !hybridEnabled) warnings.push('sync_mode_configured_but_hybrid_sync_disabled');
@@ -141,6 +143,19 @@ function buildInstanceSyncStatus(options = {}) {
141
143
  brain: Array.isArray(worker.brain) ? worker.brain : [],
142
144
  brainPrune: worker.brainPrune && typeof worker.brainPrune === 'object' ? worker.brainPrune : null,
143
145
  },
146
+ // S-30 (WP-0, remote-machine-hub-sessions-2026-10-22.html): the machine registry's rollup —
147
+ // how many boxes this hub knows and where they stand. The CALLER supplies it
148
+ // (options.machines, built by lib/machine-registry.js summarizeMachines) because this builder
149
+ // is pure: it opens no handle and probes nothing. Absent input reads as "no machines known",
150
+ // which is the truth for a hub with no registry rows.
151
+ machines: {
152
+ count: Number(machinesInput.count || 0) || 0,
153
+ reachable: Number(machinesInput.reachable || 0) || 0,
154
+ unknown: Number(machinesInput.unknown || 0) || 0,
155
+ unreachable: Number(machinesInput.unreachable || 0) || 0,
156
+ unhealthy: Number(machinesInput.unhealthy || 0) || 0,
157
+ hubNetworkSuspect: machinesInput.hubNetworkSuspect === true,
158
+ },
144
159
  exchange: {
145
160
  workspaceBindingsReady: !!workspaceBindings.enabled,
146
161
  workspaceBindingCount: Array.isArray(workspaceBindings.bindings) ? workspaceBindings.bindings.length : 0,
@@ -19,6 +19,10 @@
19
19
  //
20
20
  // Pure: never mutates `args`, never reads the DB or the environment.
21
21
 
22
+ const {
23
+ CLAUDE_OPTION_SIGNATURES, CLAUDE_SHORT_OPTIONS, locateClaudeSettingsArg,
24
+ } = require('./native-lead-settings-argv');
25
+
22
26
  const MODEL_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@+\-[\]]*$/;
23
27
  const MAX_MODEL_ID_LENGTH = 200;
24
28
 
@@ -204,6 +208,160 @@ function ensureCodexResumeTarget(argv, providerThreadId) {
204
208
  return { ok: true, args };
205
209
  }
206
210
 
211
+ // --- Claude native same-thread resume normalization (D-2) ----------------------------------------
212
+ // docs/claude-routed-native-history-resume-2026-10-24.html §5 D-2, §6. A Claude lead whose model or
213
+ // route changes continues the SAME native thread: the canonical source owner's provider thread is
214
+ // the authority, never a stale startup/parent selector in the recorded argv. This normalizer turns
215
+ // every conversation selector the installed CLI accepts (`--resume`/`-r`, `--continue`/`-c`,
216
+ // `--session-id`, `--from-pr`) into exactly one explicit `--resume <canonical>` placed FIRST, drops a
217
+ // fork (which would mint a different identity), and removes the original positional startup prompt so
218
+ // it cannot execute a second time. Every other option keeps its token and its operand boundary.
219
+ //
220
+ // Pure: no fs, no clock, never mutates `argv`, never reads a file, never changes model/auth.
221
+ const CLAUDE_THREAD_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
222
+ const CLAUDE_LONG_ARITY = new Map(CLAUDE_OPTION_SIGNATURES);
223
+ // Every option token that SELECTS which conversation the CLI opens, including the ones that would
224
+ // start a DIFFERENT one. Each is replaced by the canonical owner's own explicit resume: an old
225
+ // `--session-id` would START that id again (colliding with the recorded transcript), `--continue`/`-c`
226
+ // would not even name a thread, and `--from-pr` names a conversation that is not this one at all.
227
+ const CLAUDE_CONVERSATION_SELECTORS = new Set(['--resume', '--session-id', '--continue', '--from-pr']);
228
+ const CLAUDE_CONVERSATION_SELECTOR_SHORTS = new Set(['r', 'c']);
229
+ // A recorded thread is continued IN PLACE: a fork would mint a new session identity, which is not what
230
+ // this operation does, so it is dropped. `--replay-user-messages` is deliberately NOT here -- it only
231
+ // changes how output is streamed and names no conversation identity, so it is preserved untouched.
232
+ const CLAUDE_FORK_MODIFIERS = new Set(['--fork-session']);
233
+
234
+ // Commander's own option-likeness rule: a lone `-` is an OPERAND, `--` is the terminator.
235
+ function _isClaudeOptionLike(token) {
236
+ return typeof token === 'string' && token.length > 1 && token[0] === '-';
237
+ }
238
+
239
+ // One pass over an installed-CLI Claude argv, modelling Commander's operand consumption exactly
240
+ // (`combineFlagAndOptionalValue` = true, so a short option's attached remainder is its value; a
241
+ // required operand is consumed unconditionally; an optional one only when the next token is not
242
+ // option-like; a variadic one takes its first operand plus every following non-option token). An
243
+ // operand that merely SPELLS `--resume`/`--session-id` is therefore never read as a selector.
244
+ //
245
+ // Selector tokens are dropped and every OTHER token is returned untouched, in its original order, with
246
+ // its own operand. A mixed short cluster keeps its non-selector members (rebuilt, with any attached
247
+ // value) instead of losing flags: `-pc` becomes `-p`, `-cd value` becomes `-d value`, `-dp` is left
248
+ // alone (`d` takes `p` as its value, so there is no selector in it at all).
249
+ function _claudeResumeKeptTokens(args) {
250
+ const kept = [];
251
+ let index = 0;
252
+ while (index < args.length) {
253
+ const token = String(args[index]);
254
+ // A real terminator: every remaining token is positional, i.e. this launch's own prompt.
255
+ if (token === '--') break;
256
+ if (token.startsWith('--') && token.length > 2) {
257
+ const equalsAt = token.indexOf('=');
258
+ const name = equalsAt === -1 ? token : token.slice(0, equalsAt);
259
+ const inlineValue = equalsAt === -1 ? null : token.slice(equalsAt + 1);
260
+ const arity = CLAUDE_LONG_ARITY.get(name);
261
+ if (!arity) return { ok: false };
262
+ if (arity === 'boolean') {
263
+ if (inlineValue !== null) return { ok: false };
264
+ if (!CLAUDE_CONVERSATION_SELECTORS.has(name) && !CLAUDE_FORK_MODIFIERS.has(name)) kept.push(args[index]);
265
+ index += 1;
266
+ continue;
267
+ }
268
+ let end = index + 1;
269
+ if (inlineValue === null) {
270
+ if (arity === 'required') {
271
+ if (index + 1 >= args.length) return { ok: false };
272
+ end = index + 2;
273
+ } else if (arity === 'optional') {
274
+ if (args[index + 1] !== undefined && !_isClaudeOptionLike(args[index + 1])) end = index + 2;
275
+ } else { // variadic
276
+ if (index + 1 >= args.length) return { ok: false };
277
+ end = index + 2;
278
+ while (end < args.length && args[end] !== '--' && !_isClaudeOptionLike(args[end])) end += 1;
279
+ }
280
+ }
281
+ if (!CLAUDE_CONVERSATION_SELECTORS.has(name)) for (let i = index; i < end; i += 1) kept.push(args[i]);
282
+ index = end;
283
+ continue;
284
+ }
285
+ if (token.startsWith('-') && token.length > 1) {
286
+ const cluster = token.slice(1);
287
+ let position = 0;
288
+ let consumed = 1;
289
+ let rebuilt = '';
290
+ let selector = false;
291
+ let keepsOperand = false;
292
+ while (position < cluster.length) {
293
+ const character = cluster[position];
294
+ const spec = CLAUDE_SHORT_OPTIONS[character];
295
+ if (!spec) return { ok: false };
296
+ const isSelector = CLAUDE_CONVERSATION_SELECTOR_SHORTS.has(character);
297
+ const arity = spec[1];
298
+ if (arity === 'boolean') {
299
+ if (isSelector) selector = true; else rebuilt += character;
300
+ position += 1;
301
+ continue;
302
+ }
303
+ // `optional`/`required`: the whole attached remainder is this option's value, so the cluster
304
+ // has no further members.
305
+ const attached = cluster.slice(position + 1);
306
+ if (attached) {
307
+ if (isSelector) selector = true; else rebuilt += character + attached;
308
+ break;
309
+ }
310
+ if (arity === 'required') {
311
+ if (index + 1 >= args.length) return { ok: false };
312
+ consumed = 2;
313
+ } else {
314
+ consumed = args[index + 1] !== undefined && !_isClaudeOptionLike(args[index + 1]) ? 2 : 1;
315
+ }
316
+ if (isSelector) selector = true; else { rebuilt += character; keepsOperand = consumed === 2; }
317
+ break;
318
+ }
319
+ if (selector) {
320
+ // The non-selector members survive; a non-selector member that consumed the next token keeps
321
+ // that operand too.
322
+ if (rebuilt) kept.push(`-${rebuilt}`);
323
+ if (keepsOperand) kept.push(args[index + 1]);
324
+ index += consumed;
325
+ continue;
326
+ }
327
+ for (let i = index; i < index + consumed; i += 1) kept.push(args[i]);
328
+ index += consumed;
329
+ continue;
330
+ }
331
+ index += 1; // a positional token is this launch's own startup prompt: it is never replayed
332
+ }
333
+ return { ok: true, kept };
334
+ }
335
+
336
+ /**
337
+ * Normalize an installed-CLI Claude argv to resume exactly the canonical source thread.
338
+ *
339
+ * @param {string[]} argv the recorded startup/restore arguments (never mutated)
340
+ * @param {string} providerThreadId the canonical source owner's provider thread (a UUID)
341
+ * @returns {{ok:true,args:string[]}|{ok:false,reason:string}}
342
+ * `claude_resume_target_invalid` — no canonical UUID was supplied;
343
+ * `claude_resume_argv_unsupported` — malformed/ambiguous argv, an unknown option, a subcommand, or
344
+ * the two installed parsers disagreeing about an operand span.
345
+ */
346
+ function ensureClaudeResumeTarget(argv, providerThreadId) {
347
+ const args = Array.isArray(argv) ? argv : null;
348
+ const threadId = String(providerThreadId == null ? '' : providerThreadId).trim();
349
+ if (!args || !CLAUDE_THREAD_ID_RE.test(threadId)) return { ok: false, reason: 'claude_resume_target_invalid' };
350
+ for (const token of args) if (typeof token !== 'string') return { ok: false, reason: 'claude_resume_argv_unsupported' };
351
+ // The EXISTING settings/source adapter is the agreed-parse guard: a subcommand launch, an unknown
352
+ // option, or the two installed parsers disagreeing about a `--settings`/`--setting-sources` span
353
+ // is refused here instead of being reinterpreted as a conversation selector.
354
+ try { locateClaudeSettingsArg(args); }
355
+ catch { return { ok: false, reason: 'claude_resume_argv_unsupported' }; }
356
+ const scanned = _claudeResumeKeptTokens(args);
357
+ if (!scanned.ok) return { ok: false, reason: 'claude_resume_argv_unsupported' };
358
+ // The canonical resume goes FIRST, ahead of every preserved option token. That is what makes the
359
+ // result readable by the EXISTING `indexOf('--resume')` extractor even when another option's own
360
+ // operand spells `--resume`/`--session-id` (e.g. `--append-system-prompt --resume`): the first
361
+ // `--resume` in the produced argv is always this launch's canonical thread.
362
+ return { ok: true, args: ['--resume', threadId, ...scanned.kept] };
363
+ }
364
+
207
365
  // --- Lead-model pin replay (claude-resume-managed-model-pin-2026-10-16.html §6 D-4/D-7) -----------
208
366
  // A managed policy (`model=opusplan`) makes a plain `claude --resume` restart an Opus session on
209
367
  // Sonnet, so the model the user explicitly chose is stored per session (WP-0 storage, WP-1 native
@@ -324,4 +482,5 @@ function applyLaunchEffortOverride(argv, agent, effort) {
324
482
  }
325
483
 
326
484
  module.exports = { applyLaunchModelOverride, isValidOverrideModel, applyLaunchEffortOverride, isValidEffort,
327
- applyLaunchModelPin, stripModelFlags, insertBeforeCodexResume, rebindCodexForkLaunchPlan, ensureCodexResumeTarget };
485
+ applyLaunchModelPin, stripModelFlags, insertBeforeCodexResume, rebindCodexForkLaunchPlan, ensureCodexResumeTarget,
486
+ ensureClaudeResumeTarget };