create-walle 0.10.2 → 0.10.3

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 (488) hide show
  1. package/README.md +3 -1
  2. package/lib/release-schema.json +2 -2
  3. package/lib/runtime-closure-policy.json +8 -7
  4. package/package.json +1 -1
  5. package/template/coding-task-manager/api-prompts.js +45 -7
  6. package/template/coding-task-manager/bin/claude-hook.js +55 -1
  7. package/template/coding-task-manager/bin/ctm-relay-client.js +35 -12
  8. package/template/coding-task-manager/bin/land.js +2069 -0
  9. package/template/coding-task-manager/bin/memory-census.js +4 -2
  10. package/template/coding-task-manager/bin/memory-sync-verify.js +151 -0
  11. package/template/coding-task-manager/bin/probe-refusal-scope.js +530 -0
  12. package/template/coding-task-manager/db.js +156 -32
  13. package/template/coding-task-manager/lib/acp/acp-client.js +383 -0
  14. package/template/coding-task-manager/lib/acp/dsh-context-truth.js +176 -0
  15. package/template/coding-task-manager/lib/acp/dsh-launch-model.js +302 -0
  16. package/template/coding-task-manager/lib/acp/dsh-mcp-servers.js +194 -0
  17. package/template/coding-task-manager/lib/acp/dsh-prompt-lane.js +616 -0
  18. package/template/coding-task-manager/lib/acp/dsh-route-patch.js +308 -0
  19. package/template/coding-task-manager/lib/acp/dsh-session-runtime.js +522 -0
  20. package/template/coding-task-manager/lib/acp/dsh-transcript.js +395 -0
  21. package/template/coding-task-manager/lib/acp/dsh-usage-log.js +162 -0
  22. package/template/coding-task-manager/lib/acp/ndjson-stdio-transport.js +225 -0
  23. package/template/coding-task-manager/lib/adopted-host-redial.js +47 -11
  24. package/template/coding-task-manager/lib/adopted-launch-environment.js +123 -0
  25. package/template/coding-task-manager/lib/agent-auto-update-runtime.js +0 -3
  26. package/template/coding-task-manager/lib/agent-auto-update.js +26 -10
  27. package/template/coding-task-manager/lib/agent-bundle/build.js +657 -0
  28. package/template/coding-task-manager/lib/agent-bundle/codex-install.js +558 -0
  29. package/template/coding-task-manager/lib/agent-bundle/drift.js +120 -0
  30. package/template/coding-task-manager/lib/agent-bundle/gc.js +522 -0
  31. package/template/coding-task-manager/lib/agent-bundle/inventory.js +346 -0
  32. package/template/coding-task-manager/lib/agent-bundle/push.js +717 -0
  33. package/template/coding-task-manager/lib/agent-bundle/skill-transfer.js +724 -0
  34. package/template/coding-task-manager/lib/agent-bundle/spawn.js +119 -0
  35. package/template/coding-task-manager/lib/agent-bundle/store.js +568 -0
  36. package/template/coding-task-manager/lib/agent-capabilities.js +142 -12
  37. package/template/coding-task-manager/lib/agent-cli-cache.js +30 -5
  38. package/template/coding-task-manager/lib/agent-definitions.js +19 -0
  39. package/template/coding-task-manager/lib/agent-hooks-codex-toml.js +28 -16
  40. package/template/coding-task-manager/lib/agent-hooks-codex-workspace.js +9 -1
  41. package/template/coding-task-manager/lib/agent-hooks-installer.js +23 -8
  42. package/template/coding-task-manager/lib/agent-hooks-prune.js +37 -11
  43. package/template/coding-task-manager/lib/agent-hooks-shim.js +105 -0
  44. package/template/coding-task-manager/lib/agent-presets.js +84 -1
  45. package/template/coding-task-manager/lib/agent-upgrade-jobs.js +12 -1
  46. package/template/coding-task-manager/lib/agent-version-service.js +74 -11
  47. package/template/coding-task-manager/lib/all-sessions-query.js +6 -2
  48. package/template/coding-task-manager/lib/api/agent-bundles.js +436 -0
  49. package/template/coding-task-manager/lib/api/backup-retention.js +83 -0
  50. package/template/coding-task-manager/lib/api/conversation-sync-routes.js +124 -0
  51. package/template/coding-task-manager/lib/api/diagnostics.js +549 -69
  52. package/template/coding-task-manager/lib/api/fleet.js +118 -0
  53. package/template/coding-task-manager/lib/api/hub-bridge.js +243 -0
  54. package/template/coding-task-manager/lib/api/ingress.js +9 -1
  55. package/template/coding-task-manager/lib/api/landing.js +403 -0
  56. package/template/coding-task-manager/lib/api/machines.js +691 -2
  57. package/template/coding-task-manager/lib/api/memory-sync-routes.js +335 -0
  58. package/template/coding-task-manager/lib/api/misc-ops.js +8 -3
  59. package/template/coding-task-manager/lib/api/remote-sessions.js +1326 -0
  60. package/template/coding-task-manager/lib/api/session-attach.js +623 -0
  61. package/template/coding-task-manager/lib/api/session-messages.js +1384 -0
  62. package/template/coding-task-manager/lib/api/sessions.js +77 -51
  63. package/template/coding-task-manager/lib/api/setup-network.js +544 -16
  64. package/template/coding-task-manager/lib/api/setup-wizard-handlers.js +884 -0
  65. package/template/coding-task-manager/lib/api/setup.js +829 -10
  66. package/template/coding-task-manager/lib/api/walle-bridge.js +9 -1
  67. package/template/coding-task-manager/lib/attachment-reconciler-wiring.js +5 -2
  68. package/template/coding-task-manager/lib/auth-rate-limit.js +23 -0
  69. package/template/coding-task-manager/lib/auth-rules.js +292 -21
  70. package/template/coding-task-manager/lib/background-stop.js +369 -0
  71. package/template/coding-task-manager/lib/best-of-attempts.js +4 -1
  72. package/template/coding-task-manager/lib/best-of-launch-plan.js +52 -0
  73. package/template/coding-task-manager/lib/box-keep-awake.js +89 -0
  74. package/template/coding-task-manager/lib/broadcast-reason-stats.js +152 -0
  75. package/template/coding-task-manager/lib/broadcast-serialize.js +5 -0
  76. package/template/coding-task-manager/lib/build-info.js +4 -2
  77. package/template/coding-task-manager/lib/claude-model-truth.js +13 -2
  78. package/template/coding-task-manager/lib/claude-transcript-terminal-renderer.js +17 -13
  79. package/template/coding-task-manager/lib/client-diag-handler.js +64 -2
  80. package/template/coding-task-manager/lib/codex-app-server-client.js +14 -16
  81. package/template/coding-task-manager/lib/codex-configuration-history.js +55 -0
  82. package/template/coding-task-manager/lib/codex-harness-qualification.js +43 -20
  83. package/template/coding-task-manager/lib/codex-rollout-history.js +743 -0
  84. package/template/coding-task-manager/lib/coding-agent-models.js +27 -12
  85. package/template/coding-task-manager/lib/coding-cli-skill-installer.js +44 -14
  86. package/template/coding-task-manager/lib/coding-session-model-adapters.js +43 -2
  87. package/template/coding-task-manager/lib/coding-session-model-owner.js +1 -1
  88. package/template/coding-task-manager/lib/coding-session-model-public.js +7 -1
  89. package/template/coding-task-manager/lib/coding-session-model-runtime.js +23 -4
  90. package/template/coding-task-manager/lib/coding-session-model-scan.js +31 -0
  91. package/template/coding-task-manager/lib/coding-session-model-state.js +7 -0
  92. package/template/coding-task-manager/lib/conversation-freshness-diagnostics.js +280 -0
  93. package/template/coding-task-manager/lib/conversation-item-clock.js +261 -0
  94. package/template/coding-task-manager/lib/conversation-source-registry.js +5 -0
  95. package/template/coding-task-manager/lib/conversation-store-v2.js +44 -0
  96. package/template/coding-task-manager/lib/conversation-sync.js +355 -0
  97. package/template/coding-task-manager/lib/create/resolve-launch-params.js +96 -12
  98. package/template/coding-task-manager/lib/create/worker-role-profile.js +4 -4
  99. package/template/coding-task-manager/lib/create-intent-lease.js +51 -0
  100. package/template/coding-task-manager/lib/ctm-session-context-api.js +97 -6
  101. package/template/coding-task-manager/lib/db-domain-tables.js +1 -0
  102. package/template/coding-task-manager/lib/db-domains.js +1 -0
  103. package/template/coding-task-manager/lib/db-owner-dispatch-probe.js +32 -0
  104. package/template/coding-task-manager/lib/db-owner-request-counts.js +178 -0
  105. package/template/coding-task-manager/lib/db-owner-worker-client.js +68 -14
  106. package/template/coding-task-manager/lib/db-schema-source-files.js +5 -0
  107. package/template/coding-task-manager/lib/deferred-restore-attempt.js +6 -2
  108. package/template/coding-task-manager/lib/feature-manifest.js +4 -0
  109. package/template/coding-task-manager/lib/fleet-conversation-route.js +97 -0
  110. package/template/coding-task-manager/lib/fleet-fanout.js +78 -0
  111. package/template/coding-task-manager/lib/fleet-hub.js +762 -0
  112. package/template/coding-task-manager/lib/fleet-origin-follower.js +252 -0
  113. package/template/coding-task-manager/lib/fleet-resolver-effects.js +147 -0
  114. package/template/coding-task-manager/lib/fleet-resolver-tick.js +148 -0
  115. package/template/coding-task-manager/lib/fleet-resolver.js +454 -0
  116. package/template/coding-task-manager/lib/fleet-session-list-routes.js +114 -0
  117. package/template/coding-task-manager/lib/fleet-session-lists.js +672 -0
  118. package/template/coding-task-manager/lib/fleet-update.js +798 -0
  119. package/template/coding-task-manager/lib/fs-watcher-root-watch.js +141 -0
  120. package/template/coding-task-manager/lib/fs-watcher.js +58 -42
  121. package/template/coding-task-manager/lib/harness-history-transition.js +50 -9
  122. package/template/coding-task-manager/lib/headless-term-service.js +84 -8
  123. package/template/coding-task-manager/lib/hermetic-agent-launch-guard.js +10 -0
  124. package/template/coding-task-manager/lib/history-transition-read-op.js +175 -8
  125. package/template/coding-task-manager/lib/host-cpu-categories.js +310 -0
  126. package/template/coding-task-manager/lib/host-fseventsd-sampler.js +399 -0
  127. package/template/coding-task-manager/lib/host-handoff.js +16 -19
  128. package/template/coding-task-manager/lib/host-stats.js +150 -3
  129. package/template/coding-task-manager/lib/hosted-sync-worker.js +43 -320
  130. package/template/coding-task-manager/lib/hub-bridge-access.js +798 -0
  131. package/template/coding-task-manager/lib/hub-bridge-box.js +409 -0
  132. package/template/coding-task-manager/lib/hub-bridge-mcp.js +380 -0
  133. package/template/coding-task-manager/lib/hub-bridge-mesh.js +313 -0
  134. package/template/coding-task-manager/lib/hub-bridge-ops.js +721 -0
  135. package/template/coding-task-manager/lib/hub-bridge.js +398 -0
  136. package/template/coding-task-manager/lib/idle-notify.js +491 -0
  137. package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +8 -0
  138. package/template/coding-task-manager/lib/instance-manifest.js +5 -1
  139. package/template/coding-task-manager/lib/instance-sync-status.js +18 -8
  140. package/template/coding-task-manager/lib/landing/agent-rule.txt +1 -0
  141. package/template/coding-task-manager/lib/landing/batch-runner.js +366 -0
  142. package/template/coding-task-manager/lib/landing/config.js +536 -0
  143. package/template/coding-task-manager/lib/landing/ctm-view.js +256 -0
  144. package/template/coding-task-manager/lib/landing/gate.js +360 -0
  145. package/template/coding-task-manager/lib/landing/git.js +344 -0
  146. package/template/coding-task-manager/lib/landing/lease.js +247 -0
  147. package/template/coding-task-manager/lib/landing/merge-delta.js +380 -0
  148. package/template/coding-task-manager/lib/landing/proof.js +223 -0
  149. package/template/coding-task-manager/lib/landing/queue.js +998 -0
  150. package/template/coding-task-manager/lib/landing/radar.js +489 -0
  151. package/template/coding-task-manager/lib/landing/resolve-cli.js +424 -0
  152. package/template/coding-task-manager/lib/landing/state.js +263 -0
  153. package/template/coding-task-manager/lib/landing/stop.js +49 -0
  154. package/template/coding-task-manager/lib/landing/sync-cli.js +475 -0
  155. package/template/coding-task-manager/lib/landing/sync.js +456 -0
  156. package/template/coding-task-manager/lib/landing/train.js +885 -0
  157. package/template/coding-task-manager/lib/landing/window.js +57 -0
  158. package/template/coding-task-manager/lib/launch-environment.js +121 -0
  159. package/template/coding-task-manager/lib/launch-model-override.js +36 -10
  160. package/template/coding-task-manager/lib/launch-plan-pool.js +2 -1
  161. package/template/coding-task-manager/lib/launch-plan-warm-cache.js +6 -0
  162. package/template/coding-task-manager/lib/lead-model-observer.js +57 -1
  163. package/template/coding-task-manager/lib/lead-model-options.js +160 -3
  164. package/template/coding-task-manager/lib/lead-model-restore-reconciliation.js +106 -24
  165. package/template/coding-task-manager/lib/lead-model-switch-runtime.js +352 -68
  166. package/template/coding-task-manager/lib/lead-model-switch.js +114 -10
  167. package/template/coding-task-manager/lib/lead-model-transition-runtime.js +14 -2
  168. package/template/coding-task-manager/lib/lead-model-transition-store.js +24 -6
  169. package/template/coding-task-manager/lib/lead-route-preflight.js +179 -0
  170. package/template/coding-task-manager/lib/lead-switch-history-mode.js +110 -0
  171. package/template/coding-task-manager/lib/lead-wire-agent.js +7 -0
  172. package/template/coding-task-manager/lib/lifecycle-wake-experiment.js +9 -2
  173. package/template/coding-task-manager/lib/live-defer-announce.js +45 -0
  174. package/template/coding-task-manager/lib/machine-registry.js +750 -5
  175. package/template/coding-task-manager/lib/macos-capabilities.js +32 -15
  176. package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +433 -53
  177. package/template/coding-task-manager/lib/mainloop-observability.js +14 -9
  178. package/template/coding-task-manager/lib/media-draft-owner.js +16 -3
  179. package/template/coding-task-manager/lib/memory-join.js +337 -0
  180. package/template/coding-task-manager/lib/memory-sync-verify.js +393 -0
  181. package/template/coding-task-manager/lib/memory-sync.js +565 -0
  182. package/template/coding-task-manager/lib/migrations/v57-delegate-pref-mirror-opencode.js +68 -0
  183. package/template/coding-task-manager/lib/migrations/v58-subagent-launch-native-subagents.js +42 -0
  184. package/template/coding-task-manager/lib/migrations/v59-coding-session-model-state-dsh.js +73 -0
  185. package/template/coding-task-manager/lib/migrations/v60-delegate-pref-mirror-dsh.js +78 -0
  186. package/template/coding-task-manager/lib/migrations/v61-conversation-item-clock.js +71 -0
  187. package/template/coding-task-manager/lib/model-availability.js +727 -0
  188. package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +4 -6
  189. package/template/coding-task-manager/lib/model-host-receipt.js +6 -3
  190. package/template/coding-task-manager/lib/model-id-rule.js +19 -0
  191. package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
  192. package/template/coding-task-manager/lib/model-proxy/auto-mode-review.js +110 -23
  193. package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
  194. package/template/coding-task-manager/lib/model-proxy/generation-probe.js +60 -17
  195. package/template/coding-task-manager/lib/model-proxy/historical-tool-declarations.js +37 -0
  196. package/template/coding-task-manager/lib/model-proxy/lead-request-validation.js +1 -0
  197. package/template/coding-task-manager/lib/model-proxy/lead-token-admin.js +130 -0
  198. package/template/coding-task-manager/lib/model-proxy/refusal-scope.js +151 -0
  199. package/template/coding-task-manager/lib/model-proxy/request-budget-store.js +11 -2
  200. package/template/coding-task-manager/lib/model-proxy/tokens.js +69 -5
  201. package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
  202. package/template/coding-task-manager/lib/native-agent-model-args.js +25 -22
  203. package/template/coding-task-manager/lib/native-history-control.js +6 -4
  204. package/template/coding-task-manager/lib/native-lead-settings-client.js +44 -4
  205. package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +173 -36
  206. package/template/coding-task-manager/lib/online-backup-worker-client.js +2 -0
  207. package/template/coding-task-manager/lib/opencode-cli.js +480 -0
  208. package/template/coding-task-manager/lib/opencode-launch-model.js +471 -0
  209. package/template/coding-task-manager/lib/opencode-v1-plugin/model-carrier.mjs +35 -0
  210. package/template/coding-task-manager/lib/opencode-v2-plugin/server.js +118 -0
  211. package/template/coding-task-manager/lib/owner-action-manifest-data.json +1 -1
  212. package/template/coding-task-manager/lib/park-composer-draft.js +54 -0
  213. package/template/coding-task-manager/lib/park-fresh-relaunch.js +41 -10
  214. package/template/coding-task-manager/lib/perf-phase-trace.js +72 -0
  215. package/template/coding-task-manager/lib/perf-summary.js +7 -0
  216. package/template/coding-task-manager/lib/perf-tracker.js +63 -2
  217. package/template/coding-task-manager/lib/project-config.js +7 -0
  218. package/template/coding-task-manager/lib/prompt-purge-v2.js +52 -1
  219. package/template/coding-task-manager/lib/provider-login-broker.js +72 -15
  220. package/template/coding-task-manager/lib/provider-login-tab.js +10 -2
  221. package/template/coding-task-manager/lib/read-pool-client.js +89 -2
  222. package/template/coding-task-manager/lib/read-pool-worker-handle.js +29 -4
  223. package/template/coding-task-manager/lib/recent-catalog-query.js +15 -0
  224. package/template/coding-task-manager/lib/recent-session-catalog-response.js +67 -10
  225. package/template/coding-task-manager/lib/recovery-evidence.js +26 -11
  226. package/template/coding-task-manager/lib/relay-client.js +57 -3
  227. package/template/coding-task-manager/lib/relink-audit.js +34 -5
  228. package/template/coding-task-manager/lib/remote-machine-catalog.js +321 -26
  229. package/template/coding-task-manager/lib/remote-machine-create.js +576 -36
  230. package/template/coding-task-manager/lib/remote-machine-link.js +546 -59
  231. package/template/coding-task-manager/lib/remote-machine-proxy.js +143 -9
  232. package/template/coding-task-manager/lib/remote-session-forward.js +65 -7
  233. package/template/coding-task-manager/lib/remote-terminal-target.js +9 -2
  234. package/template/coding-task-manager/lib/restart-guard.js +18 -6
  235. package/template/coding-task-manager/lib/restore-seed-modes.js +87 -0
  236. package/template/coding-task-manager/lib/restored-scrollback-hydration-runtime.js +22 -2
  237. package/template/coding-task-manager/lib/resume-admission.js +17 -3
  238. package/template/coding-task-manager/lib/review-repo-registry.js +58 -19
  239. package/template/coding-task-manager/lib/root-delivery-runtime.js +2 -2
  240. package/template/coding-task-manager/lib/root-prompt-flow.js +1 -1
  241. package/template/coding-task-manager/lib/runtime-contract.js +3 -1
  242. package/template/coding-task-manager/lib/schema-indexes.js +1 -0
  243. package/template/coding-task-manager/lib/session-context-locator.js +742 -0
  244. package/template/coding-task-manager/lib/session-create/attachment.js +2 -2
  245. package/template/coding-task-manager/lib/session-create/create-phases.js +118 -0
  246. package/template/coding-task-manager/lib/session-create/delegate-worker.js +86 -16
  247. package/template/coding-task-manager/lib/session-create/dsh-session.js +930 -0
  248. package/template/coding-task-manager/lib/session-create/lead-route-launch.js +186 -13
  249. package/template/coding-task-manager/lib/session-create/native-lead-lifetime.js +55 -3
  250. package/template/coding-task-manager/lib/session-create/opencode-lead-provider.js +50 -0
  251. package/template/coding-task-manager/lib/session-create/prepared.js +162 -38
  252. package/template/coding-task-manager/lib/session-create/provider-process.js +160 -28
  253. package/template/coding-task-manager/lib/session-create/response.js +7 -7
  254. package/template/coding-task-manager/lib/session-create/structured-session.js +27 -0
  255. package/template/coding-task-manager/lib/session-create/subagent-pick-table.js +3 -1
  256. package/template/coding-task-manager/lib/session-create/subagent-pick.js +458 -9
  257. package/template/coding-task-manager/lib/session-create/transcript.js +1 -1
  258. package/template/coding-task-manager/lib/session-create/worker-budget-setup.js +21 -2
  259. package/template/coding-task-manager/lib/session-create/worker-continuation.js +7 -1
  260. package/template/coding-task-manager/lib/session-create/worker-launch-sandbox.js +39 -10
  261. package/template/coding-task-manager/lib/session-create/worker-observability.js +2 -2
  262. package/template/coding-task-manager/lib/session-empty.js +13 -10
  263. package/template/coding-task-manager/lib/session-host-manager.js +60 -0
  264. package/template/coding-task-manager/lib/session-jobs.js +3 -1
  265. package/template/coding-task-manager/lib/session-lifecycle.js +10 -4
  266. package/template/coding-task-manager/lib/session-model-action-preconditions.js +6 -2
  267. package/template/coding-task-manager/lib/session-model-intent-contract.js +11 -8
  268. package/template/coding-task-manager/lib/session-park.js +738 -0
  269. package/template/coding-task-manager/lib/session-path-grant.js +13 -4
  270. package/template/coding-task-manager/lib/session-status-ledger.js +106 -4
  271. package/template/coding-task-manager/lib/session-transport.js +17 -0
  272. package/template/coding-task-manager/lib/session-workspace-projection.js +54 -5
  273. package/template/coding-task-manager/lib/session-workspace-tracking-runtime.js +213 -1
  274. package/template/coding-task-manager/lib/skill-autocomplete-query.js +120 -3
  275. package/template/coding-task-manager/lib/skill-autocomplete.js +15 -1
  276. package/template/coding-task-manager/lib/sqlite-file-set.js +39 -0
  277. package/template/coding-task-manager/lib/standup-search-adapter.js +16 -2
  278. package/template/coding-task-manager/lib/state-sync/client-ledger.js +12 -0
  279. package/template/coding-task-manager/lib/state-sync/frame-emitter.js +319 -100
  280. package/template/coding-task-manager/lib/storage/coding-session-model-store.js +4 -1
  281. package/template/coding-task-manager/lib/storage/delegate-pref-mirror-store.js +32 -9
  282. package/template/coding-task-manager/lib/structured-session-boot-sweep.js +235 -0
  283. package/template/coding-task-manager/lib/submission-ack.js +90 -2
  284. package/template/coding-task-manager/lib/systemd-scope.js +115 -0
  285. package/template/coding-task-manager/lib/telemetry-receiver.js +39 -0
  286. package/template/coding-task-manager/lib/terminal-attach-geometry.js +985 -0
  287. package/template/coding-task-manager/lib/terminal-bootstrap.js +30 -0
  288. package/template/coding-task-manager/lib/terminal-draft-operation.js +357 -0
  289. package/template/coding-task-manager/lib/terminal-input-admission.js +17 -1
  290. package/template/coding-task-manager/lib/terminal-input-prepared.js +563 -0
  291. package/template/coding-task-manager/lib/transcript-cache-temps.js +171 -0
  292. package/template/coding-task-manager/lib/transcript-frame-cache.js +8 -3
  293. package/template/coding-task-manager/lib/transcript-source.js +36 -4
  294. package/template/coding-task-manager/lib/transcript-store.js +9 -0
  295. package/template/coding-task-manager/lib/typing-interaction-trace.js +106 -3
  296. package/template/coding-task-manager/lib/usage-ledger/backfill.js +40 -5
  297. package/template/coding-task-manager/lib/usage-ledger/client.js +4 -1
  298. package/template/coding-task-manager/lib/usage-ledger/dsh-parse.js +152 -0
  299. package/template/coding-task-manager/lib/usage-ledger/dsh-read.js +73 -0
  300. package/template/coding-task-manager/lib/usage-ledger/ingest.js +34 -6
  301. package/template/coding-task-manager/lib/usage-ledger/live.js +35 -8
  302. package/template/coding-task-manager/lib/usage-ledger/read.js +29 -20
  303. package/template/coding-task-manager/lib/usage-ledger/schema.js +61 -1
  304. package/template/coding-task-manager/lib/usage-ledger/task-coordinator.js +4 -2
  305. package/template/coding-task-manager/lib/wake-skip-reasons.js +1 -0
  306. package/template/coding-task-manager/lib/walle/turn.js +1 -1
  307. package/template/coding-task-manager/lib/walle-history-serve.js +838 -0
  308. package/template/coding-task-manager/lib/walle-kill-authority.js +2 -2
  309. package/template/coding-task-manager/lib/walle-log-rotation.js +164 -1
  310. package/template/coding-task-manager/lib/walle-mcp-auto-config.js +56 -2
  311. package/template/coding-task-manager/lib/walle-row-history.js +495 -0
  312. package/template/coding-task-manager/lib/walle-supervisor.js +33 -14
  313. package/template/coding-task-manager/lib/watch-worker-client.js +203 -0
  314. package/template/coding-task-manager/lib/worker-terminal-output.js +5 -2
  315. package/template/coding-task-manager/lib/ws/hub-bridge.js +35 -0
  316. package/template/coding-task-manager/lib/ws/lifecycle.js +6 -11
  317. package/template/coding-task-manager/lib/ws/misc.js +1 -1
  318. package/template/coding-task-manager/lib/ws/state-sync.js +106 -4
  319. package/template/coding-task-manager/lib/ws/terminal-attach.js +91 -0
  320. package/template/coding-task-manager/oauth-proxy.js +112 -115
  321. package/template/coding-task-manager/package-lock.json +10 -94
  322. package/template/coding-task-manager/package.json +4 -3
  323. package/template/coding-task-manager/protocol/message-table.json +10 -0
  324. package/template/coding-task-manager/protocol/messages.js +10 -0
  325. package/template/coding-task-manager/protocol/ws-schema.json +20 -0
  326. package/template/coding-task-manager/protocol/ws-schema.md +2 -0
  327. package/template/coding-task-manager/public/css/app.css +170 -0
  328. package/template/coding-task-manager/public/css/walle-session.css +4 -0
  329. package/template/coding-task-manager/public/index.html +473 -243
  330. package/template/coding-task-manager/public/js/agent-command.js +86 -0
  331. package/template/coding-task-manager/public/js/app/backups-panel.js +111 -5
  332. package/template/coding-task-manager/public/js/app/coding-agent-upgrade.js +103 -4
  333. package/template/coding-task-manager/public/js/app/machines-page.js +1328 -8
  334. package/template/coding-task-manager/public/js/app/models-tab.js +113 -38
  335. package/template/coding-task-manager/public/js/app/restart-banner.js +53 -0
  336. package/template/coding-task-manager/public/js/app/worktrees-panel.js +184 -2
  337. package/template/coding-task-manager/public/js/background-work.js +196 -27
  338. package/template/coding-task-manager/public/js/coding-session-model-presentation.js +138 -10
  339. package/template/coding-task-manager/public/js/core/active-session-groups.js +179 -0
  340. package/template/coding-task-manager/public/js/core/pending-create.js +238 -0
  341. package/template/coding-task-manager/public/js/core/recent-machine-source.js +228 -0
  342. package/template/coding-task-manager/public/js/core/session-commands.js +180 -14
  343. package/template/coding-task-manager/public/js/dashboard-client-function-order.json +8 -0
  344. package/template/coding-task-manager/public/js/document-review-links.js +9 -1
  345. package/template/coding-task-manager/public/js/file-resolution-client.js +52 -1
  346. package/template/coding-task-manager/public/js/history-app-seam.js +22 -2
  347. package/template/coding-task-manager/public/js/machine-pool-shared.js +558 -3
  348. package/template/coding-task-manager/public/js/message-renderer.js +4 -3
  349. package/template/coding-task-manager/public/js/model-picker.js +3 -1
  350. package/template/coding-task-manager/public/js/native-subagent-review.js +15 -2
  351. package/template/coding-task-manager/public/js/new-session-agent-grid.js +346 -0
  352. package/template/coding-task-manager/public/js/pasted-content.js +236 -0
  353. package/template/coding-task-manager/public/js/pe-private-content-lifetime.js +39 -7
  354. package/template/coding-task-manager/public/js/private-model-lifetime.js +6 -2
  355. package/template/coding-task-manager/public/js/prompts.js +11 -10
  356. package/template/coding-task-manager/public/js/protocol.js +10 -0
  357. package/template/coding-task-manager/public/js/restart-banner-policy.js +29 -1
  358. package/template/coding-task-manager/public/js/session-model-picker.js +447 -57
  359. package/template/coding-task-manager/public/js/session-search-utils.js +18 -2
  360. package/template/coding-task-manager/public/js/session-usage-view.js +2 -1
  361. package/template/coding-task-manager/public/js/session-workspace-tracking.js +51 -1
  362. package/template/coding-task-manager/public/js/state-sync-client.js +10 -1
  363. package/template/coding-task-manager/public/js/stream-view.js +6 -5
  364. package/template/coding-task-manager/public/js/terminal/conv-only-session.js +287 -0
  365. package/template/coding-task-manager/public/js/terminal/runtime-09.js +2 -0
  366. package/template/coding-task-manager/public/js/terminal/runtime-10.js +5 -5
  367. package/template/coding-task-manager/public/js/terminal/runtime-12.js +8 -1
  368. package/template/coding-task-manager/public/js/terminal/runtime-16.js +4 -0
  369. package/template/coding-task-manager/public/js/terminal/runtime-20.js +1 -1
  370. package/template/coding-task-manager/public/js/terminal-attachment-draft.js +67 -13
  371. package/template/coding-task-manager/public/js/terminal-image-orchestration.js +52 -6
  372. package/template/coding-task-manager/public/js/terminal-wheel.js +23 -3
  373. package/template/coding-task-manager/public/js/walle/chat-conversation.js +4 -4
  374. package/template/coding-task-manager/public/js/walle.js +6 -0
  375. package/template/coding-task-manager/public/js/ws/live-dispatch.js +10 -0
  376. package/template/coding-task-manager/public/m/app.css +140 -0
  377. package/template/coding-task-manager/public/m/app.js +507 -68
  378. package/template/coding-task-manager/public/m/index.html +17 -0
  379. package/template/coding-task-manager/public/m/machines.js +838 -4
  380. package/template/coding-task-manager/public/m/private-api.js +31 -1
  381. package/template/coding-task-manager/public/usage.html +1 -0
  382. package/template/coding-task-manager/scripts/dev-route-fixture/synthetic-route-upstream.js +104 -0
  383. package/template/coding-task-manager/scripts/dev-route-fixture.js +339 -0
  384. package/template/coding-task-manager/scripts/opencode-lead-route-probe.js +195 -0
  385. package/template/coding-task-manager/scripts/opencode-v2-contract-probe/fake-anthropic.js +168 -0
  386. package/template/coding-task-manager/scripts/opencode-v2-contract-probe/probe-plugin/server.js +95 -0
  387. package/template/coding-task-manager/scripts/opencode-v2-contract-probe.js +673 -0
  388. package/template/coding-task-manager/server.js +1950 -11679
  389. package/template/coding-task-manager/session-integrity.js +64 -4
  390. package/template/coding-task-manager/workers/db-owner-worker.js +19 -2
  391. package/template/coding-task-manager/workers/online-backup-worker.js +1 -0
  392. package/template/coding-task-manager/workers/read-pool-model-collector.js +51 -3
  393. package/template/coding-task-manager/workers/read-pool-worker.js +76 -6
  394. package/template/coding-task-manager/workers/session-host-pool-process.js +6 -6
  395. package/template/coding-task-manager/workers/session-host-process.js +5 -5
  396. package/template/coding-task-manager/workers/watch-worker.js +105 -0
  397. package/template/coding-task-manager/workers/worktree-worker.js +168 -33
  398. package/template/ctm-relay/lib/protocol.js +14 -1
  399. package/template/package-lock.json +2 -2
  400. package/template/package.json +1 -1
  401. package/template/shared/backup-retention.js +82 -0
  402. package/template/shared/conversation-admission.js +2 -0
  403. package/template/shared/conversation-provenance.js +3 -7
  404. package/template/shared/conversation-storage-v2-contract.js +5 -0
  405. package/template/shared/linked-worktree.js +51 -0
  406. package/template/shared/macos-fda-target.js +57 -0
  407. package/template/shared/orphan-parents.js +55 -0
  408. package/template/shared/release-schema.json +2 -2
  409. package/template/shared/storage/contract.js +3 -0
  410. package/template/wall-e/adapters/imessage.js +12 -0
  411. package/template/wall-e/agent.js +71 -4
  412. package/template/wall-e/api-walle.js +21 -1
  413. package/template/wall-e/bin/train-gemma-e4b-tooluse.js +2 -2
  414. package/template/wall-e/bin/walle-mcp-stdio.js +5 -5
  415. package/template/wall-e/brain/brain-join.js +859 -0
  416. package/template/wall-e/brain/brain-sync-membership.js +20 -0
  417. package/template/wall-e/brain/brain-sync-store.js +131 -77
  418. package/template/wall-e/brain/brain-sync-v2-schema.js +203 -0
  419. package/template/wall-e/brain/brain-sync-v2.js +578 -0
  420. package/template/wall-e/brain/delegate-model-prefs-store.js +13 -4
  421. package/template/wall-e/brain/deterministic-id.js +51 -0
  422. package/template/wall-e/brain/memories.js +2 -2
  423. package/template/wall-e/brain/migrations.js +40 -6
  424. package/template/wall-e/brain/model-identity-migration.js +1 -1
  425. package/template/wall-e/brain/owner-call-whitelist.js +23 -7
  426. package/template/wall-e/brain/schema.js +45 -85
  427. package/template/wall-e/brain.js +171 -35
  428. package/template/wall-e/chat.js +3 -3
  429. package/template/wall-e/coding/agent-shell-env.js +7 -2
  430. package/template/wall-e/coding/prompt-bundle.js +66 -21
  431. package/template/wall-e/config/main-loop-exemptions.json +2 -2
  432. package/template/wall-e/http/admin-api.js +37 -0
  433. package/template/wall-e/http/gws-api.js +4 -0
  434. package/template/wall-e/http/helper-rules-api.js +7 -1
  435. package/template/wall-e/http/model-admin.js +8 -1
  436. package/template/wall-e/http/model-delegate-pref-admin.js +3 -3
  437. package/template/wall-e/http/model-proxy-providers.js +174 -1
  438. package/template/wall-e/http/model-route-health.js +157 -0
  439. package/template/wall-e/http/sync-api.js +278 -464
  440. package/template/wall-e/jobs/job-runner.js +28 -1
  441. package/template/wall-e/lib/brain-owner-op-priority.js +9 -1
  442. package/template/wall-e/lib/ctm-data-dir.js +26 -0
  443. package/template/wall-e/lib/derivation-home.js +147 -0
  444. package/template/wall-e/lib/helper-rules/evaluate.js +50 -9
  445. package/template/wall-e/lib/helper-rules/grade.js +51 -8
  446. package/template/wall-e/lib/helper-rules/lead-call-hook.js +38 -5
  447. package/template/wall-e/lib/helper-rules/store.js +4 -2
  448. package/template/wall-e/lib/landing-mcp-tools.js +170 -0
  449. package/template/wall-e/lib/mcp-client-config.js +133 -14
  450. package/template/wall-e/lib/mcp-gateway-migrate.js +28 -64
  451. package/template/wall-e/lib/mcp-integration.js +58 -13
  452. package/template/wall-e/lib/parent-death.js +7 -1
  453. package/template/wall-e/lib/scheduler-worker-jobs.js +6 -4
  454. package/template/wall-e/llm/delegate-lead-prefs.js +16 -1
  455. package/template/wall-e/llm/delegate-vendor-default.js +12 -5
  456. package/template/wall-e/llm/model-roles.js +1 -1
  457. package/template/wall-e/llm/native-delegate-selection.js +56 -8
  458. package/template/wall-e/llm/worker-delegate-args.js +72 -7
  459. package/template/wall-e/llm/worker-mcp-routing.js +66 -17
  460. package/template/wall-e/loops/brain-custom-writes.js +19 -0
  461. package/template/wall-e/loops/ingest.js +17 -1
  462. package/template/wall-e/loops/think.js +19 -4
  463. package/template/wall-e/mcp-contract.js +222 -6
  464. package/template/wall-e/mcp-protocol-contract.js +40 -5
  465. package/template/wall-e/mcp-server.js +128 -29
  466. package/template/wall-e/mcp-worker-schemas.js +5 -5
  467. package/template/wall-e/memory/ctm-context-client.js +85 -0
  468. package/template/wall-e/memory/ctm-session-context.js +9 -3
  469. package/template/wall-e/memory/hub-bridge-client.js +388 -0
  470. package/template/wall-e/memory/replica-origin.js +56 -0
  471. package/template/wall-e/memory/session-pool.js +135 -0
  472. package/template/wall-e/runtime/live-capabilities.js +8 -3
  473. package/template/wall-e/server.js +4 -2
  474. package/template/wall-e/shared/release-schema.json +2 -2
  475. package/template/wall-e/shared/sqlite-file-set.js +39 -0
  476. package/template/wall-e/shared/storage/contract.js +3 -0
  477. package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
  478. package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +7 -1
  479. package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +4 -4
  480. package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +6 -6
  481. package/template/wall-e/skills/_templates/fleet-sync/VERSION +1 -0
  482. package/template/wall-e/skills/_templates/fleet-sync/released-sha256.json +16 -0
  483. package/template/wall-e/skills/_templates/fleet-sync/templates/claude/SKILL.md +64 -0
  484. package/template/wall-e/skills/_templates/fleet-sync/templates/codex/SKILL.md +64 -0
  485. package/template/wall-e/tools/chrome-automation.js +6 -0
  486. package/template/wall-e/training/setup-cuda-train-env.sh +2 -2
  487. package/template/wall-e/workers/brain-owner-worker.js +18 -4
  488. package/template/wall-e/workers/mcp-gateway-process.js +5 -5
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+ /**
3
+ * WP-3 watch worker — design doc typing-delay-session-list-broadcast-storm-2026-11-01.html
4
+ * §5 D-4, §6 (WP-3), §8 WP-3, §9 S-3..S-8.
5
+ *
6
+ * Every `fs.watch()` directory handle CTM owns used to live on the main thread's libuv loop.
7
+ * On macOS libuv serves all of them from ONE FSEventStream per `uv_loop_t`, and
8
+ * `uv__fsevents_close` blocks the calling thread on `uv_sem_wait` until libuv's CF thread has
9
+ * destroyed + recreated + `FSEventStreamStart`ed that stream with every remaining path
10
+ * (src/unix/fsevents.c). With ~690 directory watchers open, one `close()` costs 0.4-1.6 s of
11
+ * RPC to fseventsd, on the thread that also has to echo keystrokes and paint frames — the
12
+ * 2026-11-01 23:52Z incident sample has 1,330/1,339 main-thread samples in
13
+ * `HandleWrap::Close -> uv__fsevents_close -> uv_sem_wait`.
14
+ *
15
+ * libuv keeps that CF state PER LOOP, so a `worker_threads` Worker gets its own stream and its
16
+ * own semaphore. This file runs inside that worker and owns every directory watcher; a blocked
17
+ * close then stalls only this thread.
18
+ *
19
+ * Protocol over `parentPort` (main → worker):
20
+ * { op: 'watch', id, dir } create one non-recursive watcher (persistent:false — the same
21
+ * contract the main thread's `fs.watch` callers used)
22
+ * { op: 'unwatch', id } close + forget the handle, then ack (see below)
23
+ * { op: 'shutdown' } stop processing; the main side terminates the worker, so the
24
+ * per-handle closes are NOT required at exit (§9 S-8: closes at
25
+ * exit are the ones the process reclaims for free)
26
+ * worker → main:
27
+ * { op: 'event', id, eventType, filename|null } one raw fs.watch callback
28
+ * { op: 'error', id, code, message, sync?: true } sync:true = `fs.watch` threw synchronously
29
+ * (ENOENT and friends) — the owner must NOT schedule a reattach
30
+ * timer for those, matching its old synchronous try/catch
31
+ * { op: 'unwatched', id } the handle is closed; the main thread's close() already returned
32
+ *
33
+ * The filename is stringified (or null) so it survives structured clone.
34
+ */
35
+ const fs = require('node:fs');
36
+ const { parentPort } = require('node:worker_threads');
37
+
38
+ const handles = new Map(); // id -> fs.FSWatcher
39
+ let shuttingDown = false;
40
+
41
+ function post(message) {
42
+ if (!parentPort) return;
43
+ try {
44
+ parentPort.postMessage(message);
45
+ } catch {
46
+ /* expected: the port is already closed when the main thread tears this worker down */
47
+ }
48
+ }
49
+
50
+ function errorFields(error) {
51
+ return {
52
+ code: (error && error.code) || '',
53
+ message: (error && error.message) || String(error),
54
+ };
55
+ }
56
+
57
+ function attach(id, dir) {
58
+ if (handles.has(id)) return;
59
+ let handle;
60
+ try {
61
+ handle = fs.watch(dir, { persistent: false }, (eventType, filename) => {
62
+ post({ op: 'event', id, eventType, filename: filename == null ? null : String(filename) });
63
+ });
64
+ } catch (error) {
65
+ post({ op: 'error', id, sync: true, ...errorFields(error) });
66
+ return;
67
+ }
68
+ // F-2 (clock-free tests r3): a synchronous try/catch is NOT an 'error' listener. libuv
69
+ // broadcasts one FSEventStreamStart failure (UV_EMFILE) to EVERY watcher in the loop, on the
70
+ // async path — without this listener the EventEmitter would throw out of the callback.
71
+ handle.on('error', (error) => {
72
+ handles.delete(id);
73
+ try {
74
+ handle.close();
75
+ } catch {
76
+ /* expected: an already-errored FSWatcher may throw again on close */
77
+ }
78
+ post({ op: 'error', id, ...errorFields(error) });
79
+ });
80
+ handles.set(id, handle);
81
+ }
82
+
83
+ function detach(id) {
84
+ const handle = handles.get(id);
85
+ if (handle) {
86
+ handles.delete(id);
87
+ try {
88
+ handle.close();
89
+ } catch {
90
+ /* expected: a handle whose 'error' listener already closed it may throw on the second close */
91
+ }
92
+ }
93
+ post({ op: 'unwatched', id });
94
+ }
95
+
96
+ function onMessage(message) {
97
+ if (shuttingDown || !message || typeof message !== 'object') return;
98
+ if (message.op === 'watch') attach(Number(message.id), String(message.dir));
99
+ else if (message.op === 'unwatch') detach(Number(message.id));
100
+ else if (message.op === 'shutdown') shuttingDown = true;
101
+ }
102
+
103
+ if (parentPort) parentPort.on('message', onMessage);
104
+
105
+ module.exports = { onMessage };
@@ -1,8 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const { parentPort } = require('node:worker_threads');
3
+ const { parentPort, isMainThread } = require('node:worker_threads');
4
4
  const fs = require('node:fs');
5
5
  const path = require('node:path');
6
+ const { setTimeout: sleepPromise } = require('node:timers/promises');
7
+ const { boolFlag, registerFlag } = require('../lib/env-flags');
6
8
  const gitUtils = require('../git-utils');
7
9
  const createWorktreeGitObservation = require('../lib/worktree-git-observation');
8
10
  const branchInventory = require('../lib/branch-inventory');
@@ -23,7 +25,39 @@ let closing = false;
23
25
  let enqueued = 0;
24
26
  let completed = 0;
25
27
  let failed = 0;
26
- const queue = [];
28
+ let dropped = 0;
29
+
30
+ // D-1 (docs/session-create-box-restart-and-git-lane-starvation-2026-10-30.html §5, §6.1):
31
+ // the serial git lane serves the waiting human first. An op a human is blocked on is
32
+ // "interactive"; everything else is "background". The class is decided HERE, from a static
33
+ // table, never by the caller — callers already disagree about budgets (the review registry
34
+ // allows 10 s per statusV2, the create flow 10 min + 30 s), so a caller-set priority drifts.
35
+ const INTERACTIVE_OPS = new Set([
36
+ 'createWorktree', 'removeWorktree', 'finishWorktree', 'mergeWorktree', 'deleteBranch',
37
+ 'recoverWorktree', 'syncWorktree', 'pushAndCreatePR', 'projectRows', 'scanRoots',
38
+ 'resolveFileReference', 'lsTree', 'resolveRef', 'getBranch',
39
+ ]);
40
+ // Every other op in runOp's switch, explicitly acknowledged as background. The coverage test
41
+ // (tests/worktree-worker-queue.test.js, AC-1c) parses runOp's `case` labels and fails on a
42
+ // label that is in neither set (or in both), so a new op cannot be added unclassified.
43
+ const BACKGROUND_OPS_ACK = new Set([
44
+ 'listRichWorktrees', 'mainSha', 'resolveSyncTarget', 'repoFacts', 'statusV2', 'observeWorktree',
45
+ 'listWorktrees', 'gitRemoteKeys', 'richWorktreeStatusForPath', 'gatherGitFacts',
46
+ 'listReviewDocuments', 'findHtmlInWorktrees', 'getUnmergedCommitList', 'pruneGhosts',
47
+ 'syncAllWorktrees', 'resolveMainBranch', 'getDefaultReviewBase', 'getDefaultDiffStat',
48
+ 'getDiffStat', 'getDiffStatVsRef', 'bestOfDiff', 'untrackedDiff', 'untrackedCount',
49
+ '__testSleep', // test-only op — see runOp's case; unreachable outside the test env gate
50
+ ]);
51
+
52
+ function opClass(op, payload = {}) {
53
+ // Test-only: the queue tests need both classes out of one op name (`__testSleep`).
54
+ if (op === '__testSleep' && payload?.interactive === true) return 'interactive';
55
+ return INTERACTIVE_OPS.has(op) ? 'interactive' : 'background';
56
+ }
57
+
58
+ // D-1: two FIFO queues, drained interactive-first. One lane, so there is no preemption and the
59
+ // worst case for a human op is the one op already running (P-3).
60
+ const queues = { interactive: [], background: [] };
27
61
  const { observeWorktree } = createWorktreeGitObservation({
28
62
  git: (cwd, args, options = {}) => gitUtils.git(cwd, args, { ...options, env: gitUtils._gitProbeEnv() }),
29
63
  gitStrict: (cwd, args, options = {}) => gitUtils.gitStrict(cwd, args, { ...options, env: gitUtils._gitProbeEnv() }),
@@ -47,13 +81,60 @@ function serializeError(error) {
47
81
  };
48
82
  }
49
83
 
84
+ function unsupportedOpError(op) {
85
+ const error = new Error(`Unsupported worktree worker op: ${op}`);
86
+ error.code = 'CTM_WORKTREE_OP_UNSUPPORTED';
87
+ return error;
88
+ }
89
+
90
+ // D-2a: the one shape a task that will never run is answered with. The client deletes its
91
+ // pending entry BEFORE it posts `cancel` (lib/worktree-worker-client.js:99-104,117), so this
92
+ // answer is simply ignored there; it exists for a caller that is still waiting.
93
+ function cancelledError() {
94
+ return Object.assign(new Error('Worktree worker request cancelled'), {
95
+ code: 'CTM_WORKTREE_WORKER_CANCELLED',
96
+ cancelled: true,
97
+ });
98
+ }
99
+
100
+ // Test-only op body (see runOp's `__testSleep` case): a sleep that honours the task's signal.
101
+ async function sleepHonoringSignal(ms, signal) {
102
+ if (signal?.aborted) throw cancelledError();
103
+ try {
104
+ await sleepPromise(Math.max(0, Number(ms) || 0), undefined, { signal });
105
+ } catch (err) {
106
+ if (err?.name === 'AbortError') throw cancelledError();
107
+ throw err;
108
+ }
109
+ }
110
+
111
+ registerFlag('CTM_WORKTREE_WORKER_TEST_OPS', {
112
+ kind: 'bool',
113
+ default: false,
114
+ desc: 'Test-only: enable the __testSleep fake op in the worktree worker so the queue tests '
115
+ + 'drive workers/worktree-worker.js without git. Requires NODE_ENV=test as well; never set outside tests.',
116
+ expires: null,
117
+ });
118
+
119
+ function testOpsEnabled() {
120
+ return process.env.NODE_ENV === 'test' && boolFlag('CTM_WORKTREE_WORKER_TEST_OPS', false);
121
+ }
122
+
50
123
  function status() {
124
+ const heads = [queues.interactive[0], queues.background[0]].filter(Boolean);
51
125
  return {
52
126
  active: activeTask?.op || '',
53
- queued: queue.length,
127
+ activeClass: activeTask?.taskClass || '',
128
+ queued: queues.interactive.length + queues.background.length,
129
+ queuedInteractive: queues.interactive.length,
130
+ queuedBackground: queues.background.length,
131
+ oldestQueuedMs: heads.length
132
+ ? Math.max(0, Date.now() - Math.min(...heads.map(task => task.enqueuedAt)))
133
+ : 0,
54
134
  enqueued,
55
135
  completed,
56
136
  failed,
137
+ dropped,
57
138
  errors: errorSink.snapshot(),
58
139
  ...gitUtils._getGitProbeMetrics(),
59
140
  };
@@ -358,18 +439,32 @@ async function runOp(op, payload = {}, context = {}) {
358
439
  return untrackedDiffer.untrackedDiff(cwd, payload || {});
359
440
  case 'untrackedCount':
360
441
  return { total: (await untrackedDiffer.listUntracked(cwd)).length };
361
- default: {
362
- const error = new Error(`Unsupported worktree worker op: ${op}`);
363
- error.code = 'CTM_WORKTREE_OP_UNSUPPORTED';
364
- throw error;
442
+ case '__testSleep': {
443
+ // Test-only (tests/worktree-worker-queue.test.js AC-0a..c/AC-1a/AC-1b/meta): drives the
444
+ // real queue of THIS file without touching git. Off unless NODE_ENV=test and
445
+ // CTM_WORKTREE_WORKER_TEST_OPS=1, where the op is otherwise unsupported.
446
+ if (!testOpsEnabled()) throw unsupportedOpError(op);
447
+ context.emitProgress?.({ stage: 'test-start', tag: payload.tag });
448
+ await sleepHonoringSignal(payload.ms, context.signal);
449
+ return { tag: payload.tag };
365
450
  }
451
+ default:
452
+ throw unsupportedOpError(op);
366
453
  }
367
454
  }
368
455
 
369
456
  function pump() {
370
457
  if (activeTask || closing) return;
371
- const task = queue.shift();
372
- if (!task) return;
458
+ // D-1: the oldest interactive task first, otherwise the oldest background one (FIFO within a
459
+ // class). D-2b: a task whose signal already aborted never starts — it is answered like a
460
+ // cancel and skipped, so a dropped caller never costs the lane real work.
461
+ let task;
462
+ for (;;) {
463
+ task = queues.interactive.shift() || queues.background.shift();
464
+ if (!task) return;
465
+ if (!task.controller.signal.aborted) break;
466
+ answerCancelled(task);
467
+ }
373
468
  activeTask = task;
374
469
  try { parentPort.postMessage({ type: 'status', status: status() }); } catch (_) {}
375
470
  const emitProgress = (event) => {
@@ -391,9 +486,32 @@ function pump() {
391
486
  });
392
487
  }
393
488
 
489
+ // D-2a: a queued task leaves its queue the moment its caller stops waiting (splice by index,
490
+ // O(queue)); the caller is answered now instead of at the front of a lane it no longer wants.
491
+ function findQueued(requestId) {
492
+ for (const name of ['interactive', 'background']) {
493
+ const index = queues[name].findIndex(candidate => candidate.requestId === requestId);
494
+ if (index !== -1) return { name, index };
495
+ }
496
+ return null;
497
+ }
498
+
499
+ function answerCancelled(task) {
500
+ dropped += 1;
501
+ try {
502
+ parentPort.postMessage({
503
+ type: 'response',
504
+ requestId: task.requestId,
505
+ error: serializeError(cancelledError()),
506
+ status: status(),
507
+ });
508
+ } catch (_) { /* expected: the parent port is already closed during shutdown */ }
509
+ }
510
+
394
511
  function closeWhenIdle() {
395
512
  closing = true;
396
- queue.length = 0;
513
+ queues.interactive.length = 0;
514
+ queues.background.length = 0;
397
515
  if (activeTask) {
398
516
  setTimeout(closeWhenIdle, 10).unref?.();
399
517
  return;
@@ -402,27 +520,44 @@ function closeWhenIdle() {
402
520
  setImmediate(() => parentPort.close());
403
521
  }
404
522
 
405
- parentPort.on('message', (message) => {
406
- if (!message || typeof message !== 'object') return;
407
- if (message.type === 'request') {
408
- enqueued += 1;
409
- queue.push({
410
- requestId: message.requestId,
411
- op: message.op,
412
- payload: message.payload || {},
413
- controller: new AbortController(),
414
- });
415
- pump();
416
- } else if (message.type === 'cancel') {
417
- const task = activeTask?.requestId === message.requestId
418
- ? activeTask
419
- : queue.find(candidate => candidate.requestId === message.requestId);
420
- try { task?.controller.abort(); } catch (_) {}
421
- } else if (message.type === 'status') {
422
- parentPort.postMessage({ type: 'status', status: status() });
423
- } else if (message.type === 'close') {
424
- closeWhenIdle();
425
- }
426
- });
523
+ if (!isMainThread) {
524
+ parentPort.on('message', (message) => {
525
+ if (!message || typeof message !== 'object') return;
526
+ if (message.type === 'request') {
527
+ enqueued += 1;
528
+ const task = {
529
+ requestId: message.requestId,
530
+ op: message.op,
531
+ payload: message.payload || {},
532
+ controller: new AbortController(),
533
+ enqueuedAt: Date.now(),
534
+ };
535
+ task.taskClass = opClass(task.op, task.payload);
536
+ queues[task.taskClass].push(task);
537
+ pump();
538
+ } else if (message.type === 'cancel') {
539
+ // D-2c: cancelling the RUNNING task only aborts its controller. The lane is not freed
540
+ // early — the next task starts when this one settles.
541
+ if (activeTask?.requestId === message.requestId) {
542
+ try { activeTask.controller.abort(); } catch (_) {}
543
+ return;
544
+ }
545
+ // D-2a: a QUEUED task is removed and answered at once.
546
+ const queued = findQueued(message.requestId);
547
+ if (!queued) return;
548
+ const [task] = queues[queued.name].splice(queued.index, 1);
549
+ answerCancelled(task);
550
+ } else if (message.type === 'status') {
551
+ parentPort.postMessage({ type: 'status', status: status() });
552
+ } else if (message.type === 'close') {
553
+ closeWhenIdle();
554
+ }
555
+ });
556
+
557
+ parentPort.postMessage({ type: 'ready', status: status() });
558
+ }
427
559
 
428
- parentPort.postMessage({ type: 'ready', status: status() });
560
+ // The queue tests (tests/worktree-worker-queue.test.js, AC-1c) require this module from the
561
+ // main thread to check runOp's `case` labels against the classification tables; loaded as a
562
+ // worker thread this export is inert.
563
+ module.exports = { INTERACTIVE_OPS, BACKGROUND_OPS_ACK, opClass };
@@ -11,7 +11,13 @@ const { EventEmitter } = require('node:events');
11
11
  const TYPE = Object.freeze({
12
12
  HELLO: 0x01, // client->relay once after upgrade; payload JSON {v:1, client:'cli', version, caps:{enc:[]}}
13
13
  READY: 0x02, // relay->client; payload JSON {tunnel_id, slug, hostname, quota:{bytes_30d, used_30d, reset_at}}
14
- OPEN: 0x03, // relay->client; payload JSON {kind:'http'|'upgrade', method, url, headers:{...}, remote:'1.2.3.4'}
14
+ // OPEN 0x03 is relay->client; payload JSON. Rev 2 has one kind:
15
+ // { kind: 'tls', sni: 'mac.r.walle.sh', alpn: ['h2','http/1.1'] | [], remote: '203.0.113.7', remote_port: 51234 }
16
+ // The first DATA frames carry the buffered ClientHello bytes verbatim; then the
17
+ // rest of the TCP stream. Rev 1's 'http' and 'upgrade' kinds are removed by
18
+ // WP-11 (relay edge) / WP-14 (box terminator); a receiver RSTs any kind outside
19
+ // OPEN_KINDS with {code:'client_error'}.
20
+ OPEN: 0x03,
15
21
  HEAD: 0x04, // client->relay; payload JSON {status, headers:{...}} (first frame of every response)
16
22
  DATA: 0x05, // either direction; payload = raw bytes
17
23
  END: 0x06, // either direction; half-close (no more DATA from the sender)
@@ -26,6 +32,12 @@ const TYPE = Object.freeze({
26
32
  // the frame and count it in stats().reservedFrames.
27
33
  });
28
34
 
35
+ // Kinds an OPEN frame may carry. Rev 2 has exactly one ('tls'); WP-11 (relay
36
+ // edge) and WP-14 (box terminator) delete rev 1's 'http'/'upgrade' paths and
37
+ // reduce this list to ['tls'] in the same pass. Until then every kind here is
38
+ // accepted, and a receiver RSTs anything else with {code:'client_error'}.
39
+ const OPEN_KINDS = Object.freeze(['tls', 'http', 'upgrade']);
40
+
29
41
  const INITIAL_WINDOW = 256 * 1024; // DATA bytes in flight per stream before WINDOW is required
30
42
  const MAX_FRAME = 64 * 1024; // max DATA payload per frame; the encoder splits larger writes
31
43
  const MAX_STREAMS_PER_TUNNEL = 512; // the 513th concurrent OPEN is RST {code:'client_error'} and NOT created
@@ -496,6 +508,7 @@ function createMux(transport, options = {}) {
496
508
 
497
509
  module.exports = {
498
510
  TYPE,
511
+ OPEN_KINDS,
499
512
  INITIAL_WINDOW,
500
513
  MAX_FRAME,
501
514
  MAX_STREAMS_PER_TUNNEL,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "walle",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "walle",
9
- "version": "0.10.2",
9
+ "version": "0.10.3",
10
10
  "hasInstallScript": true,
11
11
  "dependencies": {
12
12
  "pg": "^8.22.0"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "walle",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "private": true,
5
5
  "description": "Wall-E — your personal digital twin",
6
6
  "walleRuntime": {
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ // Backup retention policy shared by CTM (db.js cleanOldBackups) and Wall-E (brain.js
4
+ // cleanOldBackups). Design: coding-task-manager/docs/backup-retention-days-setting-2026-10-31.html
5
+ // §5 R-1..R-6, §6.
6
+ //
7
+ // Policy (R-1): group backups by the UTC date of `createdAt` and keep EVERY backup whose day is
8
+ // among the N most recent days that have a backup. Days without a backup do not count (Borg /
9
+ // Proxmox keep-daily semantics), so downtime never empties the window and no count floor is
10
+ // needed. The UTC date matches the day key both daily schedulers already use
11
+ // (`new Date().toISOString().slice(0, 10)`).
12
+ //
13
+ // Pure: no fs, no clock. Callers list their own backups and delete the expired rows as a set.
14
+
15
+ const DEFAULT_BACKUP_RETENTION_DAYS = 3;
16
+ const MIN_BACKUP_RETENTION_DAYS = 1;
17
+ const MAX_BACKUP_RETENTION_DAYS = 365;
18
+ const BACKUP_RETENTION_SETTING_KEY = 'backup_retention_days';
19
+
20
+ function _inRange(n) {
21
+ return Number.isInteger(n) && n >= MIN_BACKUP_RETENTION_DAYS && n <= MAX_BACKUP_RETENTION_DAYS;
22
+ }
23
+
24
+ /** Read path (R-5): any stored value -> integer in [1,365]; anything invalid -> the default. Never throws. */
25
+ function normalizeRetentionDays(value) {
26
+ let n = NaN;
27
+ if (typeof value === 'number') n = value;
28
+ else if (typeof value === 'string' && /^\d+$/.test(value.trim())) n = Number(value.trim());
29
+ return _inRange(n) ? n : DEFAULT_BACKUP_RETENTION_DAYS;
30
+ }
31
+
32
+ /** Write path (R-6): strict. A missing, fractional, padded or out-of-range value is rejected, never defaulted. */
33
+ function parseRetentionDaysInput(value) {
34
+ let n = NaN;
35
+ if (typeof value === 'number') n = value;
36
+ else if (typeof value === 'string' && /^\d+$/.test(value)) n = Number(value);
37
+ if (_inRange(n)) return { ok: true, days: n };
38
+ return {
39
+ ok: false,
40
+ error: 'retention_days_invalid',
41
+ min: MIN_BACKUP_RETENTION_DAYS,
42
+ max: MAX_BACKUP_RETENTION_DAYS,
43
+ };
44
+ }
45
+
46
+ function _utcDayKey(createdAt) {
47
+ if (createdAt === null || createdAt === undefined || createdAt === '') return null;
48
+ const ms = createdAt instanceof Date ? createdAt.getTime() : new Date(createdAt).getTime();
49
+ if (!Number.isFinite(ms)) return null;
50
+ return new Date(ms).toISOString().slice(0, 10);
51
+ }
52
+
53
+ /**
54
+ * R-1..R-3. Returns the EXPIRED rows (same object references), newest first. Rows whose
55
+ * createdAt does not parse are never expired and do not consume a day slot. The input array
56
+ * is not mutated.
57
+ * @param {Array<{name: string, createdAt: string|number|Date}>} backups
58
+ * @param {*} days retention days (normalized; invalid -> default)
59
+ */
60
+ function selectExpiredBackups(backups, days) {
61
+ const keepDays = normalizeRetentionDays(days);
62
+ const dated = [];
63
+ for (const row of Array.isArray(backups) ? backups : []) {
64
+ const day = _utcDayKey(row && row.createdAt);
65
+ if (day) dated.push({ row, day, ms: new Date(row.createdAt instanceof Date ? row.createdAt.getTime() : row.createdAt).getTime() });
66
+ }
67
+ const keptDays = new Set([...new Set(dated.map((d) => d.day))].sort().reverse().slice(0, keepDays));
68
+ return dated
69
+ .filter((d) => !keptDays.has(d.day))
70
+ .sort((a, b) => b.ms - a.ms)
71
+ .map((d) => d.row);
72
+ }
73
+
74
+ module.exports = {
75
+ DEFAULT_BACKUP_RETENTION_DAYS,
76
+ MIN_BACKUP_RETENTION_DAYS,
77
+ MAX_BACKUP_RETENTION_DAYS,
78
+ BACKUP_RETENTION_SETTING_KEY,
79
+ normalizeRetentionDays,
80
+ parseRetentionDaysInput,
81
+ selectExpiredBackups,
82
+ };
@@ -59,6 +59,8 @@ const PROVIDER_ALIASES = Object.freeze({
59
59
  anthropic: 'claude',
60
60
  claude: 'claude',
61
61
  'claude-desktop': 'claude',
62
+ // DeepSeek Harness: CTM writes its transcript as Claude-shaped rows (deepseek-harness-acp-agent-2026-10-28.html D-8).
63
+ dsh: 'claude',
62
64
  openai: 'openai',
63
65
  codex: 'openai',
64
66
  gemini: 'gemini',
@@ -80,12 +80,8 @@ const PEER_RELAY_PREFIX_RE = /^(Another Claude session sent a message:|<agent-me
80
80
  // tool tags, task-notification summaries, system-reminder blocks wherever
81
81
  // they appear -- user text AND tool_result previews).
82
82
  const SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
83
- // Corrected 2026-09-27 (WP-3 live capture, fix round 1): a REAL pasted-content closing tag
84
- // repeats the `id` attribute -- `</pasted_content id="824e">`, not the bare `</pasted_content>`
85
- // this regex required (verified against a real bracketed-paste capture; the closing tag never
86
- // matched, so the whole envelope leaked through unstripped). `(?:\s[^>]*)?` accepts that
87
- // attribute (or any other) on the closing tag without requiring it.
88
- const PASTED_CONTENT_RE = /<pasted_content id="[^"]*">([\s\S]*?)<\/pasted_content(?:\s[^>]*)?>/g;
83
+ // Paste-wrapper recognition moved to coding-task-manager/public/js/pasted-content.js, Claude
84
+ // Code's strict rule (docs/hub-viewed-box-conv-freshness-and-paste-render-2026-10-31.html D-5).
89
85
  const IDE_SELECTION_RE = /<ide_selection>[\s\S]*?<\/ide_selection>/g;
90
86
  const TASK_NOTIFICATION_RE = /^<task-notification>([\s\S]*?)<\/task-notification>/i;
91
87
  const TASK_NOTIFICATION_SUMMARY_RE = /<summary>([\s\S]*?)<\/summary>/i;
@@ -291,7 +287,7 @@ module.exports = {
291
287
  claudeQueuedPromptEntry, userRowKind,
292
288
  PROVENANCE, classifyUserProvenance, ENVELOPES, BUILTIN_LOCAL_COMMANDS, parseCommandEcho, UNKNOWN_TAG_RE,
293
289
  COMMAND_ENVELOPE_RE, LOCAL_COMMAND_STDOUT_RE, PEER_RELAY_PREFIX_RE,
294
- SYSTEM_REMINDER_RE, PASTED_CONTENT_RE, IDE_SELECTION_RE,
290
+ SYSTEM_REMINDER_RE, IDE_SELECTION_RE,
295
291
  TASK_NOTIFICATION_RE, TASK_NOTIFICATION_SUMMARY_RE,
296
292
  BASH_INPUT_RE, BASH_STDOUT_RE, BASH_STDERR_RE,
297
293
  };
@@ -96,6 +96,11 @@ const CTM_KNOWN_CONVERSATION_TABLES = Object.freeze(new Set([
96
96
  'conversation_projection_outbox_v2',
97
97
  'conversation_projection_deletes_v2', // v36 change-feed delete log: session_id/stable_key/timestamps, no text
98
98
  'conversation_store_epoch_v2', // v36 change-feed store epoch: one row, random token
99
+ // WP-5 replica clock (key + origin/seq + timestamps, no text), declared by migration v61; not
100
+ // content-bearing. conversation_vv/conversation_local_seq are the vector and seq allocator.
101
+ 'conversation_item_clock',
102
+ 'conversation_vv',
103
+ 'conversation_local_seq',
99
104
  'conversation_storage_metrics_v2',
100
105
  'conversation_text_fts_v2',
101
106
  'conversation_events',
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ // Is this code running from a LINKED git worktree (`git worktree add`, e.g. .claude/worktrees/<name>)?
4
+ //
5
+ // Why it exists (2026-10-31 incident): a delegated worker in a feature-branch worktree ran
6
+ // `node -e "require('./server.js')"` with no CTM_DATA_DIR override. server.js booted against the
7
+ // default data dir (~/.walle/data, the user's PRIMARY), and the branch's unmerged schema migration
8
+ // (v59) ran on the live DB. main was at v58, so every primary respawn then died with SchemaAheadError.
9
+ //
10
+ // A primary install never runs from a linked worktree: the primary checkout is a plain clone (`.git` is
11
+ // a directory) and an npm install has no `.git` at all. So "linked worktree" is a precise marker for
12
+ // "unmerged branch code". The DB guards in coding-task-manager/db.js and wall-e/brain.js refuse the
13
+ // primary data dir in that case, so a branch's migrations can only reach a live DB after they land on
14
+ // main and ship, where every install applies them automatically at boot.
15
+ //
16
+ // Detection: walk up from `startDir` to the first `.git`. A linked worktree's `.git` is a FILE whose
17
+ // `gitdir:` line points into `<repo>/.git/worktrees/<name>`. Memoized per start dir; sync fs, called
18
+ // only on the (already sync) DB-open path.
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+
23
+ const _memo = new Map();
24
+
25
+ function isLinkedWorktreeCheckout(startDir = __dirname) {
26
+ const key = path.resolve(startDir);
27
+ if (_memo.has(key)) return _memo.get(key);
28
+ let answer = false;
29
+ let dir = key;
30
+ for (;;) {
31
+ const gitPath = path.join(dir, '.git');
32
+ let stat = null;
33
+ try { stat = fs.statSync(gitPath); } catch { /* expected: no .git at this level -- keep walking up */ }
34
+ if (stat) {
35
+ if (stat.isFile()) {
36
+ let text = '';
37
+ try { text = fs.readFileSync(gitPath, 'utf8'); } catch { /* expected: unreadable .git file -- treat as not linked */ }
38
+ const match = /^gitdir:\s*(.+)$/m.exec(text);
39
+ answer = !!(match && /[\\/]worktrees[\\/][^\\/]+[\\/]?$/.test(match[1].trim()));
40
+ }
41
+ break;
42
+ }
43
+ const parent = path.dirname(dir);
44
+ if (parent === dir) break;
45
+ dir = parent;
46
+ }
47
+ _memo.set(key, answer);
48
+ return answer;
49
+ }
50
+
51
+ module.exports = { isLinkedWorktreeCheckout };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ // macOS Full Disk Access (FDA) target identity — WHICH process identity the grant must name.
4
+ //
5
+ // macOS keys a TCC/FDA grant to the RESPONSIBLE process: for a launchd job that is the exec
6
+ // launchd started, and its children inherit the grant. Neither CTM nor Wall-E runs out of an
7
+ // app bundle on every host (the measured box runs both as
8
+ // `~/.walle/notarized-node/bin/node`), so a hint that always names
9
+ // `~/.walle/bundles/Coding Task Manager.app` points the user at a path that does not exist —
10
+ // and a Homebrew `node` the user grants instead (System Settings lists it as "node" too) has a
11
+ // different, versioned Cellar identity that never matches the running binary.
12
+ //
13
+ // So every FDA hint/reveal is computed from the RUNNING exec path. Pure + DI so the mapping is
14
+ // unit-testable without a real macOS.
15
+ //
16
+ // Design: docs/signin-flow-restart-settle-and-fda-target-identity-2026-10-26.html §5 D-4..D-7.
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ // `<X>.app/Contents/MacOS/<name>` — an app bundle's exec. Anything else is a plain binary.
22
+ const APP_EXEC_RE = /^(.*\.app)\/Contents\/MacOS\/[^/]+$/;
23
+
24
+ /**
25
+ * Map a running exec path to the identity an FDA grant must name.
26
+ *
27
+ * @param {string} execPath the running executable (process.execPath, or the Wall-E daemon exec)
28
+ * @param {{ realpathSync?: (p: string) => string, existsSync?: (p: string) => boolean }} [deps]
29
+ * @returns {{ kind: 'app'|'binary', path: string, displayName: string, exists: boolean }|null}
30
+ * null when execPath is empty / not a string. An app exec maps to its `.app` directory; any
31
+ * other exec maps to its realpath (the UNRESOLVED path when realpath throws). `exists` follows
32
+ * existsSync on the resolved path (a throw reads as false).
33
+ */
34
+ function fdaTargetForExec(execPath, { realpathSync = fs.realpathSync, existsSync = fs.existsSync } = {}) {
35
+ if (typeof execPath !== 'string' || execPath === '') return null;
36
+ const appMatch = APP_EXEC_RE.exec(execPath);
37
+ let kind;
38
+ let targetPath;
39
+ let displayName;
40
+ if (appMatch) {
41
+ kind = 'app';
42
+ targetPath = appMatch[1];
43
+ displayName = path.basename(targetPath, '.app');
44
+ } else {
45
+ kind = 'binary';
46
+ targetPath = execPath;
47
+ try { targetPath = realpathSync(targetPath); }
48
+ catch { /* expected: broken symlink / unreadable parent — the unresolved exec path is still the identity to name */ }
49
+ displayName = path.basename(targetPath);
50
+ }
51
+ let exists = false;
52
+ try { exists = !!existsSync(targetPath); }
53
+ catch { exists = false; /* expected: an unreadable parent is not "exists" — never reveal what we cannot confirm */ }
54
+ return { kind, path: targetPath, displayName, exists };
55
+ }
56
+
57
+ module.exports = { fdaTargetForExec };