@adhdev/daemon-core 0.9.82-rc.40 → 0.9.82-rc.401

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 (417) hide show
  1. package/dist/boot/daemon-lifecycle.d.ts +9 -0
  2. package/dist/boot/process-hardening.d.ts +50 -0
  3. package/dist/build-info.d.ts +37 -0
  4. package/dist/chat/source-machine.d.ts +166 -0
  5. package/dist/chat/source-resolver.d.ts +104 -0
  6. package/dist/chat/subscription-updates.d.ts +1 -0
  7. package/dist/cli-adapter-types.d.ts +68 -2
  8. package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
  9. package/dist/cli-adapters/cli-state-engine.d.ts +199 -0
  10. package/dist/cli-adapters/provider-cli-adapter.d.ts +125 -64
  11. package/dist/cli-adapters/provider-cli-parse.d.ts +5 -0
  12. package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
  13. package/dist/cli-adapters/provider-cli-shared.d.ts +74 -0
  14. package/dist/cli-adapters/pty-write-chunking.d.ts +34 -0
  15. package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
  16. package/dist/cli-adapters/resolve-executable.d.ts +14 -0
  17. package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
  18. package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
  19. package/dist/cli-adapters/terminal-screen.d.ts +4 -11
  20. package/dist/commands/chat-commands-debug-bundle.d.ts +14 -0
  21. package/dist/commands/chat-commands-read.d.ts +7 -0
  22. package/dist/commands/chat-commands-scope.d.ts +39 -0
  23. package/dist/commands/chat-commands-shared.d.ts +33 -0
  24. package/dist/commands/chat-commands-write.d.ts +14 -0
  25. package/dist/commands/chat-commands.d.ts +10 -23
  26. package/dist/commands/cli-manager.d.ts +15 -1
  27. package/dist/commands/handler.d.ts +125 -0
  28. package/dist/commands/high-family/index.d.ts +3 -0
  29. package/dist/commands/high-family/mesh-coordinator-launch.d.ts +2 -0
  30. package/dist/commands/high-family/mesh-events.d.ts +2 -0
  31. package/dist/commands/high-family/mesh-status.d.ts +2 -0
  32. package/dist/commands/high-family/types.d.ts +60 -0
  33. package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
  34. package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
  35. package/dist/commands/low-family/diagnostics.d.ts +2 -0
  36. package/dist/commands/low-family/index.d.ts +3 -0
  37. package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
  38. package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
  39. package/dist/commands/low-family/notification.d.ts +2 -0
  40. package/dist/commands/low-family/refine-config.d.ts +2 -0
  41. package/dist/commands/low-family/session-host.d.ts +2 -0
  42. package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
  43. package/dist/commands/low-family/status-meta.d.ts +2 -0
  44. package/dist/commands/low-family/types.d.ts +33 -0
  45. package/dist/commands/med-family/cli-agent.d.ts +2 -0
  46. package/dist/commands/med-family/fast-forward.d.ts +2 -0
  47. package/dist/commands/med-family/ide.d.ts +10 -0
  48. package/dist/commands/med-family/index.d.ts +3 -0
  49. package/dist/commands/med-family/mesh-crud.d.ts +2 -0
  50. package/dist/commands/med-family/mesh-host-pairing.d.ts +2 -0
  51. package/dist/commands/med-family/mesh-queue.d.ts +2 -0
  52. package/dist/commands/med-family/mesh-restart.d.ts +2 -0
  53. package/dist/commands/med-family/types.d.ts +116 -0
  54. package/dist/commands/mesh-coordinator.d.ts +85 -1
  55. package/dist/commands/router.d.ts +256 -2
  56. package/dist/commands/upgrade-helper.d.ts +41 -1
  57. package/dist/config/chat-history.d.ts +9 -0
  58. package/dist/config/config.d.ts +5 -0
  59. package/dist/config/mesh-config.d.ts +77 -1
  60. package/dist/config/mesh-json-config.d.ts +299 -0
  61. package/dist/daemon/dev-server.d.ts +0 -2
  62. package/dist/detection/ide-detector.d.ts +13 -0
  63. package/dist/detection/win32-ide-version.d.ts +37 -0
  64. package/dist/git/change-impact-config.d.ts +159 -0
  65. package/dist/git/git-commands.d.ts +11 -1
  66. package/dist/git/git-diff.d.ts +6 -0
  67. package/dist/git/git-executor.d.ts +11 -0
  68. package/dist/git/git-status.d.ts +23 -0
  69. package/dist/git/git-types.d.ts +2 -50
  70. package/dist/git/git-worktree.d.ts +71 -1
  71. package/dist/git/index.d.ts +3 -1
  72. package/dist/index.d.ts +57 -13
  73. package/dist/index.js +45987 -17082
  74. package/dist/index.js.map +1 -1
  75. package/dist/index.mjs +45750 -16986
  76. package/dist/index.mjs.map +1 -1
  77. package/dist/installer.d.ts +1 -4
  78. package/dist/ipc/local-ipc-server.d.ts +91 -0
  79. package/dist/launch.d.ts +1 -1
  80. package/dist/logging/async-batch-writer.d.ts +10 -0
  81. package/dist/logging/log-redactor.d.ts +24 -0
  82. package/dist/logging/log-tail-reader.d.ts +81 -0
  83. package/dist/logging/logger.d.ts +1 -1
  84. package/dist/mesh/contracts.d.ts +164 -0
  85. package/dist/mesh/coordinator-prompt.d.ts +89 -0
  86. package/dist/mesh/coordinator-registry.d.ts +59 -0
  87. package/dist/mesh/mesh-active-work.d.ts +174 -0
  88. package/dist/mesh/mesh-coordinator-config.d.ts +21 -0
  89. package/dist/mesh/mesh-delivery-policy.d.ts +131 -0
  90. package/dist/mesh/mesh-event-classify.d.ts +5 -0
  91. package/dist/mesh/mesh-event-forwarding.d.ts +19 -0
  92. package/dist/mesh/mesh-event-trace.d.ts +21 -0
  93. package/dist/mesh/mesh-events-coordinator.d.ts +4 -0
  94. package/dist/mesh/mesh-events-pending.d.ts +55 -0
  95. package/dist/mesh/mesh-events-stale.d.ts +61 -0
  96. package/dist/mesh/mesh-events-utils.d.ts +75 -0
  97. package/dist/mesh/mesh-events.d.ts +6 -49
  98. package/dist/mesh/mesh-fast-forward.d.ts +81 -0
  99. package/dist/mesh/mesh-host-ownership.d.ts +9 -0
  100. package/dist/mesh/mesh-init.d.ts +86 -0
  101. package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -4
  102. package/dist/mesh/mesh-ledger.d.ts +77 -1
  103. package/dist/mesh/mesh-missions.d.ts +164 -0
  104. package/dist/mesh/mesh-node-identity.d.ts +289 -0
  105. package/dist/mesh/mesh-queue-assignment.d.ts +95 -0
  106. package/dist/mesh/mesh-reconcile-loop.d.ts +65 -0
  107. package/dist/mesh/mesh-refine-batch.d.ts +68 -0
  108. package/dist/mesh/mesh-refine-gates.d.ts +428 -0
  109. package/dist/mesh/mesh-refine-status.d.ts +62 -0
  110. package/dist/mesh/mesh-review-inbox.d.ts +90 -0
  111. package/dist/mesh/mesh-routing.d.ts +70 -0
  112. package/dist/mesh/mesh-runtime-store.d.ts +447 -0
  113. package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -0
  114. package/dist/mesh/mesh-task-stats.d.ts +49 -0
  115. package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +30 -0
  116. package/dist/mesh/mesh-warmup-deadline.d.ts +68 -0
  117. package/dist/mesh/mesh-work-queue.d.ts +247 -5
  118. package/dist/mesh/preview-freshness.d.ts +18 -0
  119. package/dist/mesh/refine-config.d.ts +216 -0
  120. package/dist/mesh/worktree-bootstrap-config.d.ts +145 -0
  121. package/dist/providers/acp-provider-instance.d.ts +5 -0
  122. package/dist/providers/approval-utils.d.ts +20 -0
  123. package/dist/providers/chat-message-normalization.d.ts +13 -1
  124. package/dist/providers/cli-provider-instance.d.ts +167 -3
  125. package/dist/providers/contracts.d.ts +139 -6
  126. package/dist/providers/external-sources.d.ts +71 -0
  127. package/dist/providers/manual-attendance.d.ts +63 -0
  128. package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
  129. package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
  130. package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
  131. package/dist/providers/native-history/constants.d.ts +12 -0
  132. package/dist/providers/native-history/dispatcher.d.ts +27 -0
  133. package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
  134. package/dist/providers/native-history/index.d.ts +13 -0
  135. package/dist/providers/provider-instance-manager.d.ts +14 -0
  136. package/dist/providers/provider-instance.d.ts +23 -1
  137. package/dist/providers/provider-loader.d.ts +26 -4
  138. package/dist/providers/provider-trust.d.ts +31 -0
  139. package/dist/providers/read-chat-contract.d.ts +29 -0
  140. package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
  141. package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +86 -0
  142. package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
  143. package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
  144. package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
  145. package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
  146. package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
  147. package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
  148. package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
  149. package/dist/providers/sdk/v1/index.d.ts +30 -0
  150. package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
  151. package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
  152. package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
  153. package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
  154. package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
  155. package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
  156. package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
  157. package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
  158. package/dist/providers/spec/adapter.d.ts +91 -0
  159. package/dist/providers/spec/cli-adapter.d.ts +223 -0
  160. package/dist/providers/spec/evaluator.d.ts +45 -0
  161. package/dist/providers/spec/fsm-driver.d.ts +407 -0
  162. package/dist/providers/spec/fsm-evaluator.d.ts +76 -0
  163. package/dist/providers/spec/fsm-loader.d.ts +14 -0
  164. package/dist/providers/spec/fsm-types.d.ts +174 -0
  165. package/dist/providers/spec/native-history-executor.d.ts +41 -0
  166. package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
  167. package/dist/providers/spec/route.d.ts +4 -0
  168. package/dist/providers/spec/types.d.ts +222 -0
  169. package/dist/providers/status-monitor.d.ts +7 -7
  170. package/dist/providers/transcript-v2.d.ts +176 -0
  171. package/dist/providers/types/interactive-prompt.d.ts +84 -0
  172. package/dist/providers/working-dir.d.ts +17 -0
  173. package/dist/repo-mesh-types.d.ts +420 -13
  174. package/dist/runtime-defaults.d.ts +2 -0
  175. package/dist/sessions/registry.d.ts +6 -0
  176. package/dist/shared-types-extra.d.ts +2 -4
  177. package/dist/shared-types.d.ts +59 -2
  178. package/dist/status/normalize.d.ts +1 -1
  179. package/dist/status/normalize.js +1 -0
  180. package/dist/status/normalize.js.map +1 -1
  181. package/dist/status/normalize.mjs +1 -0
  182. package/dist/status/normalize.mjs.map +1 -1
  183. package/dist/status/reporter.d.ts +2 -0
  184. package/dist/status/snapshot.d.ts +26 -0
  185. package/dist/system/hash.d.ts +8 -0
  186. package/dist/system/load-better-sqlite3.d.ts +21 -0
  187. package/dist/types.d.ts +5 -0
  188. package/package.json +7 -3
  189. package/src/agent-stream/poller.ts +2 -3
  190. package/src/agent-stream/provider-adapter.ts +1 -1
  191. package/src/boot/daemon-lifecycle.ts +63 -12
  192. package/src/boot/process-hardening.ts +89 -0
  193. package/src/build-info.ts +73 -0
  194. package/src/chat/source-machine.ts +534 -0
  195. package/src/chat/source-resolver.ts +0 -0
  196. package/src/chat/subscription-updates.ts +20 -1
  197. package/src/cli-adapter-types.d.ts +3 -1
  198. package/src/cli-adapter-types.ts +68 -2
  199. package/src/cli-adapters/cli-script-runner.ts +421 -0
  200. package/src/cli-adapters/cli-state-engine.ts +1236 -0
  201. package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
  202. package/src/cli-adapters/provider-cli-adapter.ts +909 -1144
  203. package/src/cli-adapters/provider-cli-parse.d.ts +2 -0
  204. package/src/cli-adapters/provider-cli-parse.ts +19 -0
  205. package/src/cli-adapters/provider-cli-runtime.ts +56 -7
  206. package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
  207. package/src/cli-adapters/provider-cli-shared.ts +121 -11
  208. package/src/cli-adapters/pty-transport.ts +2 -1
  209. package/src/cli-adapters/pty-write-chunking.ts +106 -0
  210. package/src/cli-adapters/raw-terminal-io.ts +252 -0
  211. package/src/cli-adapters/resolve-executable.ts +90 -0
  212. package/src/cli-adapters/session-host-transport.ts +2 -1
  213. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +53 -29
  214. package/src/cli-adapters/terminal-backends/types.ts +10 -3
  215. package/src/cli-adapters/terminal-screen.ts +16 -81
  216. package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +198 -0
  217. package/src/commands/chat-commands-debug-bundle.ts +398 -0
  218. package/src/commands/chat-commands-read.ts +2327 -0
  219. package/src/commands/chat-commands-scope.ts +54 -0
  220. package/src/commands/chat-commands-shared.ts +114 -0
  221. package/src/commands/chat-commands-write.ts +880 -0
  222. package/src/commands/chat-commands.ts +19 -1841
  223. package/src/commands/cli-manager.ts +527 -26
  224. package/src/commands/handler.ts +841 -2
  225. package/src/commands/high-family/index.ts +28 -0
  226. package/src/commands/high-family/mesh-coordinator-launch.ts +673 -0
  227. package/src/commands/high-family/mesh-events.ts +80 -0
  228. package/src/commands/high-family/mesh-status.ts +678 -0
  229. package/src/commands/high-family/types.ts +76 -0
  230. package/src/commands/low-family/coordinator-prompt.ts +72 -0
  231. package/src/commands/low-family/daemon-lifecycle.ts +107 -0
  232. package/src/commands/low-family/diagnostics.ts +57 -0
  233. package/src/commands/low-family/index.ts +37 -0
  234. package/src/commands/low-family/mesh-ledger.ts +62 -0
  235. package/src/commands/low-family/mesh-node-logs.ts +87 -0
  236. package/src/commands/low-family/notification.ts +116 -0
  237. package/src/commands/low-family/refine-config.ts +106 -0
  238. package/src/commands/low-family/session-host.ts +274 -0
  239. package/src/commands/low-family/spec-providerdev.ts +217 -0
  240. package/src/commands/low-family/status-meta.ts +112 -0
  241. package/src/commands/low-family/types.ts +39 -0
  242. package/src/commands/med-family/cli-agent.ts +238 -0
  243. package/src/commands/med-family/fast-forward.ts +230 -0
  244. package/src/commands/med-family/ide.ts +163 -0
  245. package/src/commands/med-family/index.ts +37 -0
  246. package/src/commands/med-family/mesh-crud.ts +874 -0
  247. package/src/commands/med-family/mesh-host-pairing.ts +234 -0
  248. package/src/commands/med-family/mesh-queue.ts +131 -0
  249. package/src/commands/med-family/mesh-restart.ts +92 -0
  250. package/src/commands/med-family/types.ts +120 -0
  251. package/src/commands/mesh-coordinator.ts +334 -124
  252. package/src/commands/router.ts +2516 -3221
  253. package/src/commands/stream-commands.ts +8 -0
  254. package/src/commands/upgrade-helper.ts +310 -45
  255. package/src/config/chat-history.ts +483 -24
  256. package/src/config/config.ts +12 -0
  257. package/src/config/mesh-config.ts +353 -22
  258. package/src/config/mesh-json-config.ts +617 -0
  259. package/src/config/recent-activity.ts +8 -2
  260. package/src/daemon/dev-auto-implement.ts +3 -2
  261. package/src/daemon/dev-cli-debug.ts +10 -1
  262. package/src/daemon/dev-server.ts +0 -541
  263. package/src/detection/cli-detector.ts +28 -9
  264. package/src/detection/ide-detector.ts +55 -16
  265. package/src/detection/win32-ide-version.ts +106 -0
  266. package/src/git/change-impact-config.ts +354 -0
  267. package/src/git/git-commands.ts +55 -14
  268. package/src/git/git-diff.ts +81 -11
  269. package/src/git/git-executor.ts +12 -0
  270. package/src/git/git-status.ts +574 -43
  271. package/src/git/git-types.ts +14 -62
  272. package/src/git/git-worktree.ts +261 -4
  273. package/src/git/index.ts +17 -0
  274. package/src/index.ts +171 -12
  275. package/src/installer.d.ts +1 -1
  276. package/src/installer.ts +8 -6
  277. package/src/ipc/local-ipc-server.ts +278 -0
  278. package/src/launch.d.ts +1 -1
  279. package/src/launch.ts +37 -28
  280. package/src/logging/async-batch-writer.ts +55 -0
  281. package/src/logging/command-log.ts +7 -5
  282. package/src/logging/log-redactor.ts +100 -0
  283. package/src/logging/log-tail-reader.ts +341 -0
  284. package/src/logging/logger.ts +14 -7
  285. package/src/mesh/contracts.ts +338 -0
  286. package/src/mesh/coordinator-prompt.ts +381 -33
  287. package/src/mesh/coordinator-registry.ts +121 -0
  288. package/src/mesh/mesh-active-work.ts +645 -0
  289. package/src/mesh/mesh-coordinator-config.ts +97 -0
  290. package/src/mesh/mesh-delivery-policy.ts +315 -0
  291. package/src/mesh/mesh-event-classify.ts +51 -0
  292. package/src/mesh/mesh-event-forwarding.ts +1773 -0
  293. package/src/mesh/mesh-event-trace.ts +67 -0
  294. package/src/mesh/mesh-events-coordinator.ts +31 -0
  295. package/src/mesh/mesh-events-pending.ts +590 -0
  296. package/src/mesh/mesh-events-stale.ts +389 -0
  297. package/src/mesh/mesh-events-utils.ts +443 -0
  298. package/src/mesh/mesh-events.ts +32 -1035
  299. package/src/mesh/mesh-fast-forward.ts +839 -0
  300. package/src/mesh/mesh-host-ownership.ts +73 -0
  301. package/src/mesh/mesh-init.ts +260 -0
  302. package/src/mesh/mesh-ledger-reconciliation.ts +12 -7
  303. package/src/mesh/mesh-ledger.ts +596 -102
  304. package/src/mesh/mesh-missions.ts +322 -0
  305. package/src/mesh/mesh-node-identity.ts +1887 -0
  306. package/src/mesh/mesh-queue-assignment.ts +1738 -0
  307. package/src/mesh/mesh-reconcile-loop.ts +1554 -0
  308. package/src/mesh/mesh-refine-batch.ts +205 -0
  309. package/src/mesh/mesh-refine-gates.ts +1660 -0
  310. package/src/mesh/mesh-refine-status.ts +231 -0
  311. package/src/mesh/mesh-review-inbox.ts +307 -0
  312. package/src/mesh/mesh-routing.ts +291 -0
  313. package/src/mesh/mesh-runtime-store.ts +1835 -0
  314. package/src/mesh/mesh-scheduling-runtime.ts +201 -0
  315. package/src/mesh/mesh-task-stats.ts +161 -0
  316. package/src/mesh/mesh-unresolved-forward-outbox.ts +185 -0
  317. package/src/mesh/mesh-warmup-deadline.ts +152 -0
  318. package/src/mesh/mesh-work-queue.ts +1141 -141
  319. package/src/mesh/preview-freshness.ts +118 -0
  320. package/src/mesh/refine-config.ts +423 -0
  321. package/src/mesh/worktree-bootstrap-config.ts +369 -0
  322. package/src/providers/acp-provider-instance.ts +43 -10
  323. package/src/providers/approval-utils.d.ts +5 -0
  324. package/src/providers/approval-utils.ts +57 -5
  325. package/src/providers/chat-message-normalization.ts +46 -6
  326. package/src/providers/cli-provider-instance.ts +1624 -103
  327. package/src/providers/contracts.d.ts +55 -0
  328. package/src/providers/contracts.ts +150 -6
  329. package/src/providers/extension-provider-instance.ts +12 -7
  330. package/src/providers/external-sources.ts +218 -0
  331. package/src/providers/ide-provider-instance.ts +35 -12
  332. package/src/providers/manual-attendance.ts +85 -0
  333. package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
  334. package/src/providers/native-history/claude-cli-transcript.ts +396 -0
  335. package/src/providers/native-history/codex-cli-transcript.ts +479 -0
  336. package/src/providers/native-history/constants.ts +19 -0
  337. package/src/providers/native-history/dispatcher.ts +339 -0
  338. package/src/providers/native-history/hermes-cli-transcript.ts +230 -0
  339. package/src/providers/native-history/index.ts +30 -0
  340. package/src/providers/provider-instance-manager.ts +30 -0
  341. package/src/providers/provider-instance.ts +19 -1
  342. package/src/providers/provider-loader.ts +668 -50
  343. package/src/providers/provider-schema.ts +87 -14
  344. package/src/providers/provider-trust.ts +114 -0
  345. package/src/providers/read-chat-contract.ts +76 -16
  346. package/src/providers/sdk/README.md +49 -0
  347. package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
  348. package/src/providers/sdk/v1/builders/cli/detect-status.ts +313 -0
  349. package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
  350. package/src/providers/sdk/v1/builders/cli/parse-approval.ts +250 -0
  351. package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
  352. package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
  353. package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
  354. package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
  355. package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
  356. package/src/providers/sdk/v1/index.ts +152 -0
  357. package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
  358. package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
  359. package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
  360. package/src/providers/sdk/v1/schemas/cli/provider.schema.json +504 -0
  361. package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
  362. package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
  363. package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
  364. package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
  365. package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
  366. package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
  367. package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
  368. package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
  369. package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
  370. package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
  371. package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
  372. package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
  373. package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
  374. package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
  375. package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
  376. package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
  377. package/src/providers/sdk/v1/types/cli/index.ts +365 -0
  378. package/src/providers/sdk/v1/types/common/index.ts +229 -0
  379. package/src/providers/sdk/v1/validators/index.ts +19 -0
  380. package/src/providers/sdk/v1/validators/manifest.ts +110 -0
  381. package/src/providers/sdk/v1/validators/taint.ts +309 -0
  382. package/src/providers/spec/adapter.ts +235 -0
  383. package/src/providers/spec/cli-adapter.ts +1056 -0
  384. package/src/providers/spec/evaluator.ts +407 -0
  385. package/src/providers/spec/fsm-driver.ts +1304 -0
  386. package/src/providers/spec/fsm-evaluator.ts +272 -0
  387. package/src/providers/spec/fsm-loader.ts +120 -0
  388. package/src/providers/spec/fsm-types.ts +273 -0
  389. package/src/providers/spec/native-history-executor.ts +1128 -0
  390. package/src/providers/spec/pre-launch-trust.ts +104 -0
  391. package/src/providers/spec/route.ts +51 -0
  392. package/src/providers/spec/types.ts +262 -0
  393. package/src/providers/status-monitor.d.ts +7 -7
  394. package/src/providers/status-monitor.ts +37 -22
  395. package/src/providers/transcript-v2.ts +567 -0
  396. package/src/providers/types/interactive-prompt.ts +536 -0
  397. package/src/providers/version-archive.ts +64 -24
  398. package/src/providers/working-dir.ts +23 -0
  399. package/src/repo-mesh-types.ts +629 -14
  400. package/src/runtime-defaults.ts +39 -0
  401. package/src/sessions/registry.ts +6 -0
  402. package/src/shared-types-extra.ts +2 -4
  403. package/src/shared-types.d.ts +8 -0
  404. package/src/shared-types.ts +64 -1
  405. package/src/status/builders.ts +26 -6
  406. package/src/status/normalize.ts +2 -0
  407. package/src/status/reporter.ts +19 -1
  408. package/src/status/snapshot.ts +95 -26
  409. package/src/system/hash.ts +23 -0
  410. package/src/system/host-memory.ts +29 -12
  411. package/src/system/load-better-sqlite3.ts +68 -0
  412. package/src/types.ts +5 -0
  413. package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
  414. package/dist/mesh/mesh-sync.d.ts +0 -53
  415. package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
  416. package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
  417. package/src/mesh/mesh-sync.ts +0 -111
@@ -1,8 +1,71 @@
1
- import type { GitRepoStatus, GitSubmoduleStatus, GitUpstreamFreshness } from './git-types.js';
2
- import { GitCommandError, resolveGitRepository, runGit } from './git-executor.js';
1
+ import type { DaemonBuildBehind, GitRepoStatus, GitSubmoduleStatus, GitUpstreamFreshness } from './git-types.js';
2
+ import { GIT_STATUS_TIMEOUT_MS, GitCommandError, resolveGitRepository, runGit } from './git-executor.js';
3
+ import { getDaemonBuildInfo, type DaemonBuildInfo } from '../build-info.js';
4
+ import {
5
+ type ChangeImpactConfig,
6
+ type ChangeImpactKind,
7
+ type ChangeImpactTarget,
8
+ globToRegExp,
9
+ loadChangeImpactConfig,
10
+ } from './change-impact-config.js';
3
11
 
4
12
  type ResolvedGitRepo = { workspace: string; repoRoot: string | null; isGitRepo: boolean };
5
13
 
14
+ /**
15
+ * Last successfully-collected status per workspace, used to survive a transient git
16
+ * failure (timeout, slow Windows spawn under load, a momentary lock) WITHOUT dropping
17
+ * the node out of the mesh graph. A genuine "not a git repository" answer is NOT a
18
+ * transient failure — it never populates this cache and always reports isGitRepo:false.
19
+ */
20
+ const lastKnownGoodStatus = new Map<string, GitRepoStatus>();
21
+
22
+ /**
23
+ * Memoized Change Impact evaluation, keyed by the inputs that can change the
24
+ * verdict: the scope repo path, the buildCommit..HEAD pair, and the resolved
25
+ * config source. The daemonBuildBehind probe runs on every mesh_status /
26
+ * mesh_git_status / fast_forward / git-monitor hit; without this, each hit
27
+ * re-shells `git diff` for the identical commit range. The cache stays correct
28
+ * because HEAD or a config edit perturbs the key, forcing re-evaluation.
29
+ */
30
+ interface ChangeImpactEvalEntry {
31
+ isDaemonAffecting: boolean;
32
+ affectedPackages: string[];
33
+ }
34
+ const changeImpactEvalCache = new Map<string, ChangeImpactEvalEntry>();
35
+
36
+ /**
37
+ * Best-effort cache of the loaded Change Impact config per repo root, keyed by the
38
+ * config sourceKey (path+mtime). A new sourceKey (config edited/added/removed)
39
+ * supersedes the entry. Avoids re-reading + re-parsing the config file on every
40
+ * status probe while still honoring on-disk edits.
41
+ */
42
+ interface ChangeImpactConfigCacheEntry {
43
+ sourceKey: string;
44
+ config: ChangeImpactConfig | null;
45
+ }
46
+ const changeImpactConfigCache = new Map<string, ChangeImpactConfigCacheEntry>();
47
+
48
+ /** Test seam: clear the last-known-good status cache between cases. */
49
+ export function __resetGitStatusCacheForTests(): void {
50
+ lastKnownGoodStatus.clear();
51
+ changeImpactEvalCache.clear();
52
+ changeImpactConfigCache.clear();
53
+ }
54
+
55
+ /** Test-only introspection: number of memoized Change Impact evaluations. */
56
+ export function __changeImpactEvalCacheSizeForTests(): number {
57
+ return changeImpactEvalCache.size;
58
+ }
59
+
60
+ /**
61
+ * git failure reasons that are transient/environmental rather than a real statement
62
+ * that the workspace is not a repo. On these we prefer the last-known-good status so a
63
+ * single slow git call cannot make a healthy node vanish from the graph.
64
+ */
65
+ function isTransientGitFailure(error: GitCommandError): boolean {
66
+ return error.reason === 'timeout' || error.reason === 'git_command_failed';
67
+ }
68
+
6
69
  export interface GitStatusOptions {
7
70
  timeoutMs?: number;
8
71
  /** When true, include submodule status in the result. Defaults to true. */
@@ -14,6 +77,23 @@ export interface GitStatusOptions {
14
77
  * Callers should opt into this only for convergence-critical surfaces.
15
78
  */
16
79
  refreshUpstream?: boolean;
80
+ /**
81
+ * Test/override seam for the daemon build stamp used by the stale-build
82
+ * detector. Production callers omit this so the real baked-in build commit
83
+ * (getDaemonBuildInfo) is used.
84
+ */
85
+ daemonBuildInfo?: DaemonBuildInfo;
86
+ /**
87
+ * Change Impact policy override. When provided, the daemonBuildBehind
88
+ * classifier uses this declarative config instead of auto-loading the repo's
89
+ * `.adhdev/change-impact.*` file. When omitted, getGitRepoStatus auto-loads the
90
+ * repo config (cached); when neither is present the built-in ADHDev default
91
+ * policy applies, preserving the legacy behavior exactly.
92
+ *
93
+ * Pass `null` to force the built-in default policy and skip auto-loading (used
94
+ * to assert legacy parity in tests).
95
+ */
96
+ changeImpactConfig?: ChangeImpactConfig | null;
17
97
  }
18
98
 
19
99
  interface GitUpstreamProbe {
@@ -28,9 +108,50 @@ export async function getGitRepoStatus(
28
108
  ): Promise<GitRepoStatus> {
29
109
  const lastCheckedAt = Date.now();
30
110
  const includeSubmodules = options.includeSubmodules !== false;
111
+ // Status collection fans out into several git subprocesses (status, head, stash,
112
+ // submodule, optionally fetch). On Windows the per-spawn cost alone can exceed the
113
+ // 5s default, so unless the caller pinned a timeout, give the whole collection path
114
+ // the larger status budget. A caller that explicitly sets timeoutMs (e.g. a test
115
+ // injecting 1ms to exercise the transient-failure path) is respected.
116
+ const effectiveOptions: GitStatusOptions =
117
+ options.timeoutMs === undefined ? { ...options, timeoutMs: GIT_STATUS_TIMEOUT_MS } : options;
31
118
 
32
119
  try {
33
- const repo = await resolveGitRepository(workspace, options);
120
+ const repo = await resolveGitRepository(workspace, effectiveOptions);
121
+ const status = await collectGitRepoStatus(repo, includeSubmodules, lastCheckedAt, effectiveOptions);
122
+ lastKnownGoodStatus.set(workspace, status);
123
+ return status;
124
+ } catch (error) {
125
+ const gitError = error instanceof GitCommandError
126
+ ? error
127
+ : new GitCommandError('git_command_failed', 'Failed to read Git status', { cause: error });
128
+
129
+ // A transient/environmental failure (timeout, slow-spawn-under-load) must NOT make
130
+ // a healthy node lose its repo identity and drop out of the mesh graph. Prefer the
131
+ // last status we successfully collected for this workspace, re-stamped as stale.
132
+ if (isTransientGitFailure(gitError)) {
133
+ const cached = lastKnownGoodStatus.get(workspace);
134
+ if (cached) {
135
+ return {
136
+ ...cached,
137
+ lastCheckedAt,
138
+ upstreamStatus: 'unavailable',
139
+ error: gitError.stderr || gitError.message,
140
+ reason: gitError.reason,
141
+ };
142
+ }
143
+ }
144
+
145
+ return emptyStatus(workspace, lastCheckedAt, gitError);
146
+ }
147
+ }
148
+
149
+ async function collectGitRepoStatus(
150
+ repo: ResolvedGitRepo,
151
+ includeSubmodules: boolean,
152
+ lastCheckedAt: number,
153
+ options: GitStatusOptions,
154
+ ): Promise<GitRepoStatus> {
34
155
  let parsed = await readPorcelainStatus(repo, options);
35
156
  let upstreamProbe: GitUpstreamProbe = getInitialUpstreamProbe(parsed);
36
157
 
@@ -48,6 +169,13 @@ export async function getGitRepoStatus(
48
169
  if (includeSubmodules) {
49
170
  submodules = await getSubmoduleStatuses(repo, options);
50
171
  }
172
+ const submoduleDirty = (submodules || []).some(submodule => submodule.dirty || submodule.outOfSync || !!submodule.error);
173
+ const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0
174
+ || parsed.conflictFiles.length > 0
175
+ || stashCount > 0
176
+ || submoduleDirty;
177
+
178
+ const daemonBuildBehind = await detectDaemonBuildBehind(repo, submodules, options);
51
179
 
52
180
  return {
53
181
  workspace: repo.workspace,
@@ -67,22 +195,316 @@ export async function getGitRepoStatus(
67
195
  untracked: parsed.untracked,
68
196
  deleted: parsed.deleted,
69
197
  renamed: parsed.renamed,
198
+ dirty,
70
199
  hasConflicts: parsed.conflictFiles.length > 0,
71
200
  conflictFiles: parsed.conflictFiles,
72
201
  stashCount,
73
202
  lastCheckedAt,
74
203
  submodules,
204
+ ...(daemonBuildBehind ? { daemonBuildBehind } : {}),
75
205
  };
76
- } catch (error) {
77
- if (error instanceof GitCommandError) {
78
- return emptyStatus(workspace, lastCheckedAt, error);
206
+ }
207
+
208
+ /**
209
+ * Detect whether the running daemon's build commit is a STRICT ancestor of this
210
+ * workspace's HEAD (root) or any of its submodules' HEAD. This surfaces the
211
+ * "merged a fix to main but the live daemon still ships the old bundle" gap:
212
+ * once the fix is committed, the workspace HEAD advances past the daemon's
213
+ * baked-in build commit, but the daemon keeps the old behavior until it is
214
+ * rebuilt/redeployed and restarted.
215
+ *
216
+ * Conservative by construction — returns undefined unless ancestry is provable:
217
+ * - build commit unknown → undefined
218
+ * - build commit not an object in this repo/submodule (different repo) → skip
219
+ * - build commit === HEAD (daemon is current) → undefined
220
+ * - build commit NOT an ancestor of HEAD (daemon ahead / diverged) → undefined
221
+ * Any git error is swallowed (no warning) so a flaky probe never over-warns.
222
+ */
223
+ /**
224
+ * Package names that, when changed, mean the daemon runtime is stale and must be
225
+ * rebuilt/redeployed + restarted. Everything NOT in this set (web-core,
226
+ * web-standalone, web-devconsole, terminal-render-web) is web-only — a daemon
227
+ * restart is not required for those, only a web redeploy. mcp-server runs in the
228
+ * same process surface as the daemon tooling, so it is classified as
229
+ * daemon-affecting (conservative). Unknown package → daemon-affecting.
230
+ */
231
+ const DEFAULT_DAEMON_RUNTIME_PACKAGES = [
232
+ 'daemon-core',
233
+ 'daemon-standalone',
234
+ 'session-host-core',
235
+ 'session-host-daemon',
236
+ 'terminal-mux-core',
237
+ 'terminal-mux-control',
238
+ 'terminal-mux-cli',
239
+ 'ghostty-vt-node',
240
+ 'mcp-server',
241
+ ];
242
+
243
+ const DEFAULT_WEB_ONLY_PACKAGES = [
244
+ 'web-core',
245
+ 'web-standalone',
246
+ 'web-devconsole',
247
+ 'terminal-render-web',
248
+ ];
249
+
250
+ /**
251
+ * Built-in recommended action/command per impact classification. A change-impact
252
+ * config may override any of these via `impactTargets`; missing keys fall back here.
253
+ */
254
+ const DEFAULT_IMPACT_TARGETS: Record<ChangeImpactKind, ChangeImpactTarget> = {
255
+ daemon: {
256
+ recommendedCommand: 'Redeploy + restart the daemon (a local dist rebuild alone does not update a cloud daemon).',
257
+ },
258
+ web: {
259
+ recommendedCommand: 'Redeploy the web app (no daemon restart required).',
260
+ },
261
+ none: {
262
+ recommendedCommand: 'No action required.',
263
+ },
264
+ };
265
+
266
+ /**
267
+ * The resolved Change Impact policy: the built-in ADHDev defaults merged with any
268
+ * config override. This is what the classifier consults — git-status only knows the
269
+ * facts (changed files/packages), policy is data.
270
+ */
271
+ interface ResolvedChangeImpactPolicy {
272
+ daemonRuntimePackages: Set<string>;
273
+ webOnlyPackages: Set<string>;
274
+ /** Compiled config globs for additional non-runtime root files (on top of built-ins). */
275
+ nonRuntimeRootFilePatterns: RegExp[];
276
+ impactTargets: Record<ChangeImpactKind, ChangeImpactTarget>;
277
+ }
278
+
279
+ function resolveChangeImpactPolicy(config: ChangeImpactConfig | null | undefined): ResolvedChangeImpactPolicy {
280
+ // A field provided in config REPLACES the built-in default for that field; an
281
+ // omitted field falls back to the ADHDev default, so a repo with no config (or a
282
+ // partial config) behaves exactly as before.
283
+ const daemonRuntimePackages = new Set(
284
+ config?.daemonRuntimePackages && config.daemonRuntimePackages.length
285
+ ? config.daemonRuntimePackages
286
+ : DEFAULT_DAEMON_RUNTIME_PACKAGES,
287
+ );
288
+ const webOnlyPackages = new Set(
289
+ config?.webOnlyPackages && config.webOnlyPackages.length
290
+ ? config.webOnlyPackages
291
+ : DEFAULT_WEB_ONLY_PACKAGES,
292
+ );
293
+ const nonRuntimeRootFilePatterns = (config?.nonRuntimeRootFilePatterns || []).map(globToRegExp);
294
+ const impactTargets: Record<ChangeImpactKind, ChangeImpactTarget> = {
295
+ daemon: config?.impactTargets?.daemon ?? DEFAULT_IMPACT_TARGETS.daemon,
296
+ web: config?.impactTargets?.web ?? DEFAULT_IMPACT_TARGETS.web,
297
+ none: config?.impactTargets?.none ?? DEFAULT_IMPACT_TARGETS.none,
298
+ };
299
+ return { daemonRuntimePackages, webOnlyPackages, nonRuntimeRootFilePatterns, impactTargets };
300
+ }
301
+
302
+ /**
303
+ * Root-level (non-package) files that demonstrably cannot change what the daemon
304
+ * runtime executes: convergence/verify markers, documentation, and license/notice
305
+ * text. A root commit that moves the oss gitlink while the oss commit only touched
306
+ * one of these is NOT a reason to rebuild/restart the daemon — flagging it produces
307
+ * the staleDaemonBuild false-positive this guard exists to suppress.
308
+ *
309
+ * Deliberately conservative: anything NOT matched here (root config like
310
+ * package.json / tsconfig / build scripts, `.txt` fixtures, lockfiles, unknown
311
+ * dotfiles) stays daemon-affecting, because a false-negative — staying silent when
312
+ * the daemon really IS stale — is worse than an over-warn. Markers are the common
313
+ * real-world case (e.g. `.verify-patch-equiv-rc292`), so they are matched broadly;
314
+ * docs are matched by the `docs/` prefix or a markdown/text-doc extension at a
315
+ * filename we recognize as documentation (README/CHANGELOG/LICENSE/NOTICE).
316
+ *
317
+ * Config-supplied `nonRuntimeRootFilePatterns` are matched in ADDITION to these
318
+ * built-ins, so a repo can extend (never narrow) the benign set declaratively.
319
+ */
320
+ function isNonRuntimeRootFile(file: string, policy: ResolvedChangeImpactPolicy): boolean {
321
+ const base = file.slice(file.lastIndexOf('/') + 1);
322
+ // Verify/convergence markers: dotfiles whose name signals a transient marker.
323
+ if (/^\.(?:verify|marker|converge|ff-verify|patch-equiv|live-verify)\b/i.test(base)) return true;
324
+ // Documentation living under a docs/ tree (any depth, root or nested).
325
+ if (/(?:^|\/)docs\//i.test(file)) return true;
326
+ // Recognized top-level documentation / license files.
327
+ if (/^(?:README|CHANGELOG|LICENSE|NOTICE|AUTHORS|CONTRIBUTING|CODEOWNERS)(?:\.[A-Za-z0-9]+)?$/i.test(base)) {
328
+ return true;
329
+ }
330
+ // Config-declared additional non-runtime root globs.
331
+ for (const re of policy.nonRuntimeRootFilePatterns) {
332
+ if (re.test(file)) return true;
333
+ }
334
+ return false;
335
+ }
336
+
337
+ /**
338
+ * Determine whether the changes between buildCommit..HEAD touch any daemon-runtime
339
+ * package, per the resolved policy. Returns isDaemonAffecting:true conservatively
340
+ * when the changed-file set can't be obtained or any changed path is outside the
341
+ * known web-only package set AND is not a recognized non-runtime root file.
342
+ */
343
+ async function classifyDaemonBuildChange(
344
+ repoPath: string,
345
+ buildCommit: string,
346
+ options: GitStatusOptions,
347
+ policy: ResolvedChangeImpactPolicy,
348
+ ): Promise<{ isDaemonAffecting: boolean; affectedPackages: string[] }> {
349
+ try {
350
+ const diff = await runGit(repoPath, ['diff', '--name-only', `${buildCommit}..HEAD`], options);
351
+ const files = diff.stdout
352
+ .split('\n')
353
+ .map((line) => line.trim())
354
+ .filter(Boolean);
355
+ if (files.length === 0) {
356
+ // No file diff (e.g. only merge metadata) — nothing actionable, but stay
357
+ // conservative and treat as daemon-affecting so we don't suppress a real warning.
358
+ return { isDaemonAffecting: true, affectedPackages: [] };
79
359
  }
80
- return emptyStatus(
81
- workspace,
82
- lastCheckedAt,
83
- new GitCommandError('git_command_failed', 'Failed to read Git status', { cause: error }),
84
- );
360
+ const pkgs = new Set<string>();
361
+ // A non-package path that is NOT a recognized benign root file (marker/doc).
362
+ // Only these force daemon-affecting; benign markers/docs are ignored so a
363
+ // gitlink-moving root commit over a marker-only oss commit no longer over-warns.
364
+ let sawRuntimeAmbiguousNonPackage = false;
365
+ for (const file of files) {
366
+ const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
367
+ if (!match) {
368
+ if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
369
+ continue;
370
+ }
371
+ pkgs.add(match[1]);
372
+ }
373
+ const affectedPackages = [...pkgs].sort();
374
+ // Daemon-affecting if: any runtime-ambiguous non-package file changed, any
375
+ // unknown package changed, or any explicit daemon-runtime package changed.
376
+ // The daemon is unaffected only when every changed file is either a known
377
+ // web-only package or a recognized benign root file (and at least one such
378
+ // file changed) — i.e. nothing runtime-ambiguous remains.
379
+ const allBenign =
380
+ !sawRuntimeAmbiguousNonPackage &&
381
+ affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
382
+ return { isDaemonAffecting: !allBenign, affectedPackages };
383
+ } catch {
384
+ // diff probe failed → can't prove web-only; stay conservative.
385
+ return { isDaemonAffecting: true, affectedPackages: [] };
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Resolve the Change Impact config to apply for this status read. Priority:
391
+ * - options.changeImpactConfig === null → force built-in default policy (no load).
392
+ * - options.changeImpactConfig provided → use it verbatim (override seam).
393
+ * - otherwise → auto-load the repo's `.adhdev/change-impact.*` (cached by sourceKey).
394
+ * Returns the (possibly null) config plus the sourceKey used for cache invalidation.
395
+ */
396
+ function resolveChangeImpactConfigForRepo(
397
+ repoRoot: string | null,
398
+ options: GitStatusOptions,
399
+ ): { config: ChangeImpactConfig | null; sourceKey: string } {
400
+ if (options.changeImpactConfig === null) {
401
+ return { config: null, sourceKey: 'forced-default' };
402
+ }
403
+ if (options.changeImpactConfig !== undefined) {
404
+ // Injected override — key it to its content so a different injected config
405
+ // re-evaluates rather than reusing a prior verdict.
406
+ let key = 'injected';
407
+ try {
408
+ key = `injected:${JSON.stringify(options.changeImpactConfig)}`;
409
+ } catch {
410
+ // Non-serializable override (shouldn't happen) — fall back to a constant key.
411
+ }
412
+ return { config: options.changeImpactConfig, sourceKey: key };
413
+ }
414
+ if (!repoRoot) {
415
+ return { config: null, sourceKey: 'no-repo-root' };
85
416
  }
417
+ const loaded = loadChangeImpactConfig(repoRoot);
418
+ const cached = changeImpactConfigCache.get(repoRoot);
419
+ if (cached && cached.sourceKey === loaded.sourceKey) {
420
+ return { config: cached.config, sourceKey: loaded.sourceKey };
421
+ }
422
+ // An invalid config is conservatively ignored (built-in default policy applies),
423
+ // mirroring the "fail safe" rule — never let a malformed config silence warnings.
424
+ const config = loaded.sourceType === 'repo_file' ? loaded.config ?? null : null;
425
+ changeImpactConfigCache.set(repoRoot, { sourceKey: loaded.sourceKey, config });
426
+ return { config, sourceKey: loaded.sourceKey };
427
+ }
428
+
429
+ async function detectDaemonBuildBehind(
430
+ repo: ResolvedGitRepo,
431
+ submodules: GitSubmoduleStatus[] | undefined,
432
+ options: GitStatusOptions,
433
+ ): Promise<DaemonBuildBehind | undefined> {
434
+ const build = options.daemonBuildInfo ?? getDaemonBuildInfo();
435
+ if (!build.commit || build.commit === 'unknown') return undefined;
436
+
437
+ const { config, sourceKey: configKey } = resolveChangeImpactConfigForRepo(repo.repoRoot, options);
438
+ const policy = resolveChangeImpactPolicy(config);
439
+
440
+ // Check the root repo first, then each submodule. The daemon build commit is
441
+ // baked from the daemon-core (oss submodule) HEAD, so on an adhdev
442
+ // superproject worktree the match is expected on the `oss` submodule, not the
443
+ // root — checking both keeps the helper repo-agnostic.
444
+ const scopes: Array<{ scope: string; repoPath: string }> = [
445
+ { scope: 'root', repoPath: repo.repoRoot || repo.workspace },
446
+ ];
447
+ for (const sub of submodules || []) {
448
+ if (sub.repoPath && !sub.error) scopes.push({ scope: sub.path, repoPath: sub.repoPath });
449
+ }
450
+
451
+ for (const { scope, repoPath } of scopes) {
452
+ try {
453
+ // Build commit must be a real object in THIS repo, else it's a different repo.
454
+ await runGit(repoPath, ['cat-file', '-e', `${build.commit}^{commit}`], options);
455
+ const headResult = await runGit(repoPath, ['rev-parse', 'HEAD'], options);
456
+ const head = headResult.stdout.trim();
457
+ if (!head || head === build.commit) continue;
458
+ // Strict ancestor: build commit is reachable from HEAD but is not HEAD.
459
+ await runGit(repoPath, ['merge-base', '--is-ancestor', build.commit, 'HEAD'], options);
460
+ // No throw → build commit IS an ancestor of HEAD → daemon is behind.
461
+ // Inspect WHICH packages changed in buildCommit..HEAD. A daemon rebuild/restart
462
+ // is only actually required when a daemon-runtime package changed; if only web /
463
+ // render packages changed, the daemon is unaffected and just the web deploy is
464
+ // pending. Conservative: any probe failure → treat as daemon-affecting. The
465
+ // verdict is memoized on (repoPath, buildCommit, head, config) to suppress
466
+ // re-evaluation on the hot status path.
467
+ const evalKey = `${repoPath}${build.commit}${head}${configKey}`;
468
+ let evaluated = changeImpactEvalCache.get(evalKey);
469
+ if (!evaluated) {
470
+ evaluated = await classifyDaemonBuildChange(repoPath, build.commit, options, policy);
471
+ changeImpactEvalCache.set(evalKey, evaluated);
472
+ }
473
+ const { isDaemonAffecting, affectedPackages } = evaluated;
474
+ const kind: ChangeImpactKind = isDaemonAffecting
475
+ ? 'daemon'
476
+ : affectedPackages.length > 0
477
+ ? 'web'
478
+ : 'none';
479
+ const target = policy.impactTargets[kind];
480
+ const scopeLabel = scope === 'root' ? 'workspace' : scope;
481
+ const benignDetail = affectedPackages.length > 0
482
+ ? `only web packages changed (${affectedPackages.join(', ')})`
483
+ : 'only non-runtime files changed (markers/docs)';
484
+ const warning = isDaemonAffecting
485
+ ? `Live daemon was built from ${build.commitShort} which is behind ${scopeLabel} HEAD ${head.slice(0, 7)}. ` +
486
+ `Merged code is NOT live until the daemon is rebuilt/redeployed and restarted — a local dist rebuild alone does not update a cloud daemon.`
487
+ : `Live daemon was built from ${build.commitShort} which is behind ${scopeLabel} HEAD ${head.slice(0, 7)}, ` +
488
+ `but ${benignDetail}. ` +
489
+ `Daemon restart NOT required — redeploy the web app to reflect the change.`;
490
+ return {
491
+ buildCommit: build.commit,
492
+ buildCommitShort: build.commitShort,
493
+ head,
494
+ scope,
495
+ isDaemonAffecting,
496
+ ...(affectedPackages && affectedPackages.length > 0 ? { affectedPackages } : {}),
497
+ recommendedAction: kind,
498
+ recommendedCommand: target.recommendedCommand,
499
+ warning,
500
+ };
501
+ } catch {
502
+ // cat-file / merge-base non-zero exit (commit absent or not an ancestor)
503
+ // or any git error → not a provable staleness for this scope; try next.
504
+ continue;
505
+ }
506
+ }
507
+ return undefined;
86
508
  }
87
509
 
88
510
  interface ParsedPorcelainStatus {
@@ -285,6 +707,7 @@ function emptyStatus(workspace: string, lastCheckedAt: number, error: GitCommand
285
707
  untracked: 0,
286
708
  deleted: 0,
287
709
  renamed: 0,
710
+ dirty: false,
288
711
  hasConflicts: false,
289
712
  conflictFiles: [],
290
713
  stashCount: 0,
@@ -303,44 +726,152 @@ async function getSubmoduleStatuses(
303
726
  if (!repo.repoRoot) return [];
304
727
 
305
728
  try {
306
- const result = await runGit(repo, ['submodule', 'status', '--recursive'], options);
307
- return parseSubmoduleStatusOutput(result.stdout, repo.repoRoot, options.submoduleIgnorePaths);
729
+ // Do NOT shell out to `git submodule status`. That porcelain wrapper is a
730
+ // shell script (`git-submodule`) that, per submodule, spawns several child
731
+ // `git` processes; on Windows the wrapper + per-spawn cost alone measured
732
+ // 6.9–62.4s under AV, which dominated the whole collectGitRepoStatus budget
733
+ // and stalled the mesh graph cold-open. The information it gives us — the
734
+ // gitlink sync state (path / recorded SHA / +/-/U prefix) — is fully
735
+ // derivable from plumbing commands that don't go through the shell wrapper:
736
+ // • paths ← `.gitmodules` (git config --file, plumbing)
737
+ // • expected SHA ← `git ls-tree HEAD <path>` (the gitlink the super-
738
+ // project's HEAD tree records)
739
+ // • actual SHA ← `git -C <sub> rev-parse HEAD` (already paid below by
740
+ // enrichSubmoduleWorktreeStatus for the dirty check)
741
+ // Comparing expected vs actual reproduces `+` (out of sync); a checked-out
742
+ // submodule whose worktree is absent/uninitialized reproduces `-`. The `U`
743
+ // (conflict) prefix is surfaced separately via the superproject porcelain
744
+ // status that the caller already parses, and a conflicted submodule's own
745
+ // status read here also flags it dirty — so no row is lost.
746
+ const submodules = await deriveSubmoduleGitlinkStatuses(repo, options);
747
+ await Promise.all(submodules.map(submodule => enrichSubmoduleWorktreeStatus(repo, submodule, options)));
748
+ return submodules;
308
749
  } catch {
309
750
  return [];
310
751
  }
311
752
  }
312
753
 
313
- function parseSubmoduleStatusOutput(
314
- output: string,
315
- repoRoot: string,
316
- ignorePaths?: string[],
317
- ): GitSubmoduleStatus[] {
318
- const submodules: GitSubmoduleStatus[] = [];
319
- const ignoreSet = new Set(ignorePaths || []);
754
+ /**
755
+ * Enumerate the superproject's submodules and their gitlink sync state without the
756
+ * slow `git submodule status` shell wrapper. Pure plumbing: read paths from
757
+ * `.gitmodules`, the expected (recorded) gitlink SHA from `ls-tree HEAD`, and the
758
+ * actual checked-out SHA from the submodule's own `rev-parse HEAD`.
759
+ */
760
+ async function deriveSubmoduleGitlinkStatuses(
761
+ repo: ResolvedGitRepo,
762
+ options: GitStatusOptions,
763
+ ): Promise<GitSubmoduleStatus[]> {
764
+ if (!repo.repoRoot) return [];
765
+ const paths = await readSubmodulePaths(repo, options);
766
+ const ignoreSet = new Set(options.submoduleIgnorePaths || []);
767
+ const lastCheckedAt = Date.now();
320
768
 
321
- for (const line of output.split('\n')) {
322
- if (!line.trim()) continue;
323
-
324
- // Format: [+- ]<commit> <path> (<branch>)
325
- // - = out of sync, + = dirty, ' ' = clean
326
- const match = line.match(/^([\-+\s])([0-9a-f]{40})\s+(\S+)(?:\s+\(([^)]+)\))?/);
327
- if (!match) continue;
328
-
329
- const prefix = match[1];
330
- const commit = match[2];
331
- const path = match[3];
332
-
333
- if (ignoreSet.has(path)) continue;
334
-
335
- submodules.push({
336
- path,
337
- commit,
338
- repoPath: repoRoot + '/' + path,
339
- dirty: prefix === '+',
340
- outOfSync: prefix === '-',
341
- lastCheckedAt: Date.now(),
342
- });
769
+ const entries = await Promise.all(
770
+ paths
771
+ .filter(path => !ignoreSet.has(path))
772
+ .map(async (path): Promise<GitSubmoduleStatus> => {
773
+ const repoPath = repo.repoRoot + '/' + path;
774
+ const expected = await readGitlinkExpectedSha(repo, path, options);
775
+ const actual = await readSubmoduleHeadSha(repo, repoPath, options);
776
+ // Uninitialized / no checked-out HEAD reproduces `git submodule status`'s
777
+ // `-` prefix; a present-but-divergent HEAD reproduces the `+` prefix.
778
+ const outOfSync = actual === null
779
+ ? true
780
+ : expected !== null && expected !== actual;
781
+ return {
782
+ path,
783
+ // Prefer the recorded gitlink SHA (matches the legacy column); fall back
784
+ // to the checked-out SHA so the field is never empty when both are known.
785
+ commit: expected ?? actual ?? '',
786
+ repoPath,
787
+ dirty: false,
788
+ outOfSync,
789
+ lastCheckedAt,
790
+ };
791
+ }),
792
+ );
793
+ return entries;
794
+ }
795
+
796
+ /** Read submodule paths from `.gitmodules` via plumbing (no shell wrapper). */
797
+ async function readSubmodulePaths(repo: ResolvedGitRepo, options: GitStatusOptions): Promise<string[]> {
798
+ if (!repo.repoRoot) return [];
799
+ const gitmodulesPath = repo.repoRoot + '/.gitmodules';
800
+ try {
801
+ const result = await runGit(
802
+ repo,
803
+ ['config', '--file', gitmodulesPath, '--get-regexp', '^submodule\\..*\\.path$'],
804
+ options,
805
+ );
806
+ const paths: string[] = [];
807
+ for (const line of result.stdout.split('\n')) {
808
+ // Each line: `submodule.<name>.path <path>`
809
+ const spaceIdx = line.indexOf(' ');
810
+ if (spaceIdx < 0) continue;
811
+ const value = line.slice(spaceIdx + 1).trim();
812
+ if (value) paths.push(value);
813
+ }
814
+ return paths;
815
+ } catch {
816
+ // No .gitmodules (not a superproject) or unreadable → no submodules.
817
+ return [];
343
818
  }
819
+ }
344
820
 
345
- return submodules;
821
+ /** Expected gitlink SHA recorded in the superproject HEAD tree for this submodule path. */
822
+ async function readGitlinkExpectedSha(
823
+ repo: ResolvedGitRepo,
824
+ submodulePath: string,
825
+ options: GitStatusOptions,
826
+ ): Promise<string | null> {
827
+ try {
828
+ // `ls-tree HEAD <path>` prints: `<mode> commit <sha>\t<path>` for a gitlink.
829
+ const result = await runGit(repo, ['ls-tree', 'HEAD', submodulePath], options);
830
+ const line = result.stdout.split('\n').find(l => l.trim().length > 0);
831
+ if (!line) return null;
832
+ const match = line.match(/^\s*\d+\s+commit\s+([0-9a-f]{40})\b/);
833
+ return match ? match[1] : null;
834
+ } catch {
835
+ return null;
836
+ }
346
837
  }
838
+
839
+ /** Actual checked-out HEAD SHA of a submodule, or null if uninitialized/unreadable. */
840
+ async function readSubmoduleHeadSha(
841
+ repo: ResolvedGitRepo,
842
+ repoPath: string,
843
+ options: GitStatusOptions,
844
+ ): Promise<string | null> {
845
+ try {
846
+ // Run in the submodule worktree via cwd (inside the superproject root, so the
847
+ // executor's path-inside-repo guard is satisfied) rather than resolving the
848
+ // submodule as a fresh repo — that would cost an extra `rev-parse --show-toplevel`
849
+ // spawn per submodule, which is exactly the Windows spawn cost this fix removes.
850
+ const result = await runGit(repo, ['rev-parse', 'HEAD'], { ...options, cwd: repoPath });
851
+ const sha = result.stdout.trim();
852
+ return /^[0-9a-f]{40}$/.test(sha) ? sha : null;
853
+ } catch {
854
+ return null;
855
+ }
856
+ }
857
+
858
+ async function enrichSubmoduleWorktreeStatus(
859
+ repo: ResolvedGitRepo,
860
+ submodule: GitSubmoduleStatus,
861
+ options: GitStatusOptions,
862
+ ): Promise<void> {
863
+ try {
864
+ const result = await runGit(repo, ['status', '--porcelain=v2', '--branch'], {
865
+ ...options,
866
+ cwd: submodule.repoPath,
867
+ });
868
+ const parsed = parsePorcelainV2Status(result.stdout);
869
+ const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0
870
+ || parsed.conflictFiles.length > 0;
871
+ submodule.dirty = submodule.dirty || dirty;
872
+ } catch (error) {
873
+ submodule.dirty = true;
874
+ submodule.error = formatGitError(error);
875
+ }
876
+ }
877
+