@interf/compiler 0.22.0 → 0.33.0

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 (575) hide show
  1. package/README.md +268 -0
  2. package/dist/bin-mcp.d.ts +2 -0
  3. package/dist/bin-mcp.js +63 -0
  4. package/dist/bin-runtime.d.ts +2 -0
  5. package/dist/bin-runtime.js +111 -0
  6. package/dist/cli/commands/agents.js +4 -4
  7. package/dist/cli/commands/auth.d.ts +20 -0
  8. package/dist/cli/commands/auth.js +161 -0
  9. package/dist/cli/commands/benchmark.d.ts +10 -0
  10. package/dist/cli/commands/benchmark.js +88 -0
  11. package/dist/cli/commands/build-plan.js +95 -103
  12. package/dist/cli/commands/build.d.ts +1 -1
  13. package/dist/cli/commands/build.js +16 -15
  14. package/dist/cli/commands/doctor.js +3 -3
  15. package/dist/cli/commands/graphs.d.ts +2 -0
  16. package/dist/cli/commands/graphs.js +199 -0
  17. package/dist/cli/commands/login.js +4 -6
  18. package/dist/cli/commands/logout.js +1 -1
  19. package/dist/cli/commands/mcp.d.ts +3 -2
  20. package/dist/cli/commands/mcp.js +721 -204
  21. package/dist/cli/commands/project.d.ts +2 -0
  22. package/dist/cli/commands/project.js +202 -0
  23. package/dist/cli/commands/reset.d.ts +1 -1
  24. package/dist/cli/commands/reset.js +10 -10
  25. package/dist/cli/commands/runs.js +52 -26
  26. package/dist/cli/commands/runtime.d.ts +24 -0
  27. package/dist/cli/commands/runtime.js +373 -0
  28. package/dist/cli/commands/status.d.ts +1 -0
  29. package/dist/cli/commands/status.js +33 -22
  30. package/dist/cli/commands/traces.d.ts +2 -0
  31. package/dist/cli/commands/traces.js +125 -0
  32. package/dist/cli/commands/wizard.js +155 -150
  33. package/dist/cli/index.d.ts +7 -4
  34. package/dist/cli/index.js +13 -7
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +2 -2
  37. package/dist/packages/build-plans/authoring/brief.d.ts +517 -0
  38. package/dist/packages/build-plans/authoring/brief.js +89 -0
  39. package/dist/packages/build-plans/authoring/build-plan-authoring.d.ts +10 -10
  40. package/dist/packages/build-plans/authoring/build-plan-authoring.js +66 -26
  41. package/dist/packages/build-plans/authoring/build-plan-edit-session.d.ts +1 -1
  42. package/dist/packages/build-plans/authoring/build-plan-edit-session.js +8 -2
  43. package/dist/packages/build-plans/authoring/build-plan-improvement.d.ts +9 -6
  44. package/dist/packages/build-plans/authoring/build-plan-improvement.js +39 -42
  45. package/dist/packages/build-plans/authoring/lib/build-plan-edit-utils.js +7 -7
  46. package/dist/packages/build-plans/build-plan-resolution.d.ts +1 -1
  47. package/dist/packages/build-plans/build-plan-resolution.js +3 -3
  48. package/dist/packages/build-plans/index.d.ts +1 -1
  49. package/dist/packages/build-plans/index.js +1 -1
  50. package/dist/packages/build-plans/package/build-plan-definitions.d.ts +14 -12
  51. package/dist/packages/build-plans/package/build-plan-definitions.js +40 -39
  52. package/dist/packages/build-plans/package/build-plan-helpers.d.ts +3 -2
  53. package/dist/packages/build-plans/package/build-plan-helpers.js +27 -13
  54. package/dist/packages/build-plans/package/build-plan-review-paths.d.ts +5 -5
  55. package/dist/packages/build-plans/package/build-plan-review-paths.js +15 -15
  56. package/dist/packages/build-plans/package/build-plan-stage-runner.d.ts +4 -4
  57. package/dist/packages/build-plans/package/build-plan-stage-runner.js +22 -11
  58. package/dist/packages/build-plans/package/builtin-build-plan.d.ts +5 -6
  59. package/dist/packages/build-plans/package/builtin-build-plan.js +7 -8
  60. package/dist/packages/build-plans/package/context-interface.d.ts +11 -9
  61. package/dist/packages/build-plans/package/context-interface.js +14 -33
  62. package/dist/packages/build-plans/package/interf-build-plan-package.d.ts +6 -17
  63. package/dist/packages/build-plans/package/interf-build-plan-package.js +56 -52
  64. package/dist/packages/build-plans/package/local-build-plans.d.ts +12 -10
  65. package/dist/packages/build-plans/package/local-build-plans.js +56 -31
  66. package/dist/packages/build-plans/package/user-build-plans.js +1 -1
  67. package/dist/packages/contracts/index.d.ts +4 -2
  68. package/dist/packages/contracts/index.js +2 -1
  69. package/dist/packages/contracts/lib/project-paths.d.ts +137 -0
  70. package/dist/packages/contracts/lib/project-paths.js +211 -0
  71. package/dist/packages/contracts/lib/project-schema.d.ts +160 -0
  72. package/dist/packages/contracts/lib/project-schema.js +113 -0
  73. package/dist/packages/contracts/lib/schema.d.ts +739 -80
  74. package/dist/packages/contracts/lib/schema.js +410 -75
  75. package/dist/packages/contracts/utils/parse.js +67 -0
  76. package/dist/packages/projects/index.d.ts +6 -0
  77. package/dist/packages/{project → projects}/index.js +0 -3
  78. package/dist/packages/{project → projects}/interf-detect.d.ts +12 -12
  79. package/dist/packages/{project → projects}/interf-detect.js +56 -50
  80. package/dist/packages/projects/interf.d.ts +2 -0
  81. package/dist/packages/projects/interf.js +1 -0
  82. package/dist/packages/projects/lib/schema.d.ts +79 -0
  83. package/dist/packages/projects/lib/schema.js +89 -0
  84. package/dist/packages/projects/source-config.d.ts +58 -0
  85. package/dist/packages/projects/source-config.js +352 -0
  86. package/dist/packages/projects/source-folders.d.ts +11 -0
  87. package/dist/packages/{project → projects}/source-folders.js +26 -26
  88. package/dist/packages/{engine → runtime}/action-planner.d.ts +1 -1
  89. package/dist/packages/{engine → runtime}/action-planner.js +20 -22
  90. package/dist/packages/runtime/action-values.d.ts +1 -0
  91. package/dist/packages/runtime/action-values.js +1 -0
  92. package/dist/packages/runtime/actions/errors.d.ts +2 -0
  93. package/dist/packages/runtime/actions/errors.js +12 -0
  94. package/dist/packages/runtime/actions/fields.d.ts +82 -0
  95. package/dist/packages/runtime/actions/form-builders.d.ts +14 -0
  96. package/dist/packages/runtime/actions/form-builders.js +619 -0
  97. package/dist/packages/runtime/actions/form-validators.d.ts +8 -0
  98. package/dist/packages/runtime/actions/form-validators.js +128 -0
  99. package/dist/packages/runtime/actions/helpers.d.ts +11 -0
  100. package/dist/packages/runtime/actions/helpers.js +80 -0
  101. package/dist/packages/runtime/actions/index.d.ts +8 -0
  102. package/dist/packages/runtime/actions/index.js +11 -0
  103. package/dist/packages/runtime/actions/registry.d.ts +64 -0
  104. package/dist/packages/runtime/actions/registry.js +62 -0
  105. package/dist/packages/runtime/actions/requests.d.ts +45 -0
  106. package/dist/packages/runtime/actions/requests.js +158 -0
  107. package/dist/packages/runtime/actions/schemas.d.ts +154 -0
  108. package/dist/packages/runtime/actions/schemas.js +36 -0
  109. package/dist/packages/runtime/agent-handoff.d.ts +11 -0
  110. package/dist/packages/runtime/agent-handoff.js +101 -0
  111. package/dist/packages/{engine → runtime}/agents/index.d.ts +1 -2
  112. package/dist/packages/{engine → runtime}/agents/index.js +1 -2
  113. package/dist/packages/runtime/agents/lib/args.d.ts +14 -0
  114. package/dist/packages/runtime/agents/lib/args.js +24 -0
  115. package/dist/packages/{engine → runtime}/agents/lib/constants.d.ts +4 -1
  116. package/dist/packages/runtime/agents/lib/constants.js +13 -0
  117. package/dist/packages/runtime/agents/lib/context-graph-bootstrap.d.ts +3 -0
  118. package/dist/packages/{engine/agents/lib/verifiable-context-bootstrap.js → runtime/agents/lib/context-graph-bootstrap.js} +5 -6
  119. package/dist/packages/{engine → runtime}/agents/lib/detection.d.ts +5 -0
  120. package/dist/packages/{engine → runtime}/agents/lib/detection.js +16 -7
  121. package/dist/packages/{engine → runtime}/agents/lib/execution-profile.js +8 -14
  122. package/dist/packages/{engine → runtime}/agents/lib/execution.js +14 -4
  123. package/dist/packages/{engine → runtime}/agents/lib/preflight.js +15 -13
  124. package/dist/packages/{engine → runtime}/agents/lib/render.js +4 -4
  125. package/dist/packages/runtime/agents/lib/shell-fs.d.ts +18 -0
  126. package/dist/packages/runtime/agents/lib/shell-fs.js +190 -0
  127. package/dist/packages/runtime/agents/lib/shell-paths.d.ts +16 -0
  128. package/dist/packages/runtime/agents/lib/shell-paths.js +63 -0
  129. package/dist/packages/runtime/agents/lib/shell-projection.d.ts +25 -0
  130. package/dist/packages/runtime/agents/lib/shell-projection.js +314 -0
  131. package/dist/packages/runtime/agents/lib/shell-templates.d.ts +30 -0
  132. package/dist/packages/runtime/agents/lib/shell-templates.js +452 -0
  133. package/dist/packages/runtime/agents/lib/shell-workspace.d.ts +17 -0
  134. package/dist/packages/runtime/agents/lib/shell-workspace.js +70 -0
  135. package/dist/packages/runtime/agents/lib/shells.d.ts +63 -0
  136. package/dist/packages/runtime/agents/lib/shells.js +383 -0
  137. package/dist/packages/{engine → runtime}/agents/lib/user-config.d.ts +8 -2
  138. package/dist/packages/{engine → runtime}/agents/lib/user-config.js +8 -2
  139. package/dist/packages/runtime/agents/providers/claude-code.d.ts +13 -0
  140. package/dist/packages/runtime/agents/providers/claude-code.js +45 -0
  141. package/dist/packages/runtime/agents/providers/codex.d.ts +17 -0
  142. package/dist/packages/runtime/agents/providers/codex.js +64 -0
  143. package/dist/packages/runtime/agents/providers/cursor.d.ts +9 -0
  144. package/dist/packages/runtime/agents/providers/cursor.js +24 -0
  145. package/dist/packages/runtime/agents/providers/index.d.ts +9 -0
  146. package/dist/packages/runtime/agents/providers/index.js +31 -0
  147. package/dist/packages/runtime/agents/providers/types.d.ts +50 -0
  148. package/dist/packages/{engine → runtime}/agents/registry.d.ts +13 -2
  149. package/dist/packages/{engine → runtime}/agents/registry.js +48 -10
  150. package/dist/packages/{engine → runtime}/agents/role-executors.d.ts +1 -1
  151. package/dist/packages/{engine → runtime}/agents/role-executors.js +7 -6
  152. package/dist/packages/{engine → runtime}/agents/role-router.js +7 -5
  153. package/dist/packages/runtime/auth/account-context.d.ts +52 -0
  154. package/dist/packages/runtime/auth/account-context.js +68 -0
  155. package/dist/packages/runtime/auth/auth-flow.d.ts +73 -0
  156. package/dist/packages/runtime/auth/auth-flow.js +189 -0
  157. package/dist/packages/runtime/auth/jwt-validator.d.ts +58 -0
  158. package/dist/packages/runtime/auth/jwt-validator.js +86 -0
  159. package/dist/packages/runtime/auth/keychain.d.ts +35 -0
  160. package/dist/packages/runtime/auth/keychain.js +85 -0
  161. package/dist/packages/runtime/auth/session-store.d.ts +38 -0
  162. package/dist/packages/runtime/auth/session-store.js +88 -0
  163. package/dist/packages/runtime/auth/workos-client.d.ts +58 -0
  164. package/dist/packages/runtime/auth/workos-client.js +87 -0
  165. package/dist/packages/runtime/benchmark-question-draft.d.ts +20 -0
  166. package/dist/packages/runtime/benchmark-question-draft.js +124 -0
  167. package/dist/packages/runtime/build/artifact-counts.d.ts +1 -0
  168. package/dist/packages/{engine → runtime}/build/artifact-counts.js +5 -9
  169. package/dist/packages/{engine → runtime}/build/artifact-status.d.ts +5 -5
  170. package/dist/packages/{engine → runtime}/build/artifact-status.js +25 -23
  171. package/dist/packages/runtime/build/atomic-fs.d.ts +3 -0
  172. package/dist/packages/runtime/build/atomic-fs.js +95 -0
  173. package/dist/packages/runtime/build/billing-events.d.ts +78 -0
  174. package/dist/packages/{engine → runtime}/build/billing-events.js +17 -19
  175. package/dist/packages/runtime/build/build-evidence.d.ts +15 -0
  176. package/dist/packages/runtime/build/build-evidence.js +173 -0
  177. package/dist/packages/{engine → runtime}/build/build-pipeline.d.ts +12 -8
  178. package/dist/packages/runtime/build/build-pipeline.js +304 -0
  179. package/dist/packages/{engine → runtime}/build/build-plan-primitives.d.ts +1 -1
  180. package/dist/packages/{engine → runtime}/build/build-plan-primitives.js +0 -1
  181. package/dist/packages/runtime/build/build-plan-runs.d.ts +14 -0
  182. package/dist/packages/runtime/build/build-plan-runs.js +31 -0
  183. package/dist/packages/runtime/build/build-stage-plan.d.ts +16 -0
  184. package/dist/packages/{engine → runtime}/build/build-stage-plan.js +28 -29
  185. package/dist/packages/{engine → runtime}/build/build-stage-runner.d.ts +2 -1
  186. package/dist/packages/runtime/build/build-stage-runner.js +165 -0
  187. package/dist/packages/{engine → runtime}/build/build-target.d.ts +4 -4
  188. package/dist/packages/runtime/build/build-target.js +16 -0
  189. package/dist/packages/{engine → runtime}/build/check-evaluator.d.ts +14 -16
  190. package/dist/packages/runtime/build/check-evaluator.js +575 -0
  191. package/dist/packages/runtime/build/context-graph-paths.d.ts +51 -0
  192. package/dist/packages/runtime/build/context-graph-paths.js +133 -0
  193. package/dist/packages/runtime/build/context-graph-schema.d.ts +19 -0
  194. package/dist/packages/runtime/build/context-graph-schema.js +39 -0
  195. package/dist/packages/{engine → runtime}/build/discovery.d.ts +2 -2
  196. package/dist/packages/{engine → runtime}/build/discovery.js +4 -4
  197. package/dist/packages/{engine → runtime}/build/index.d.ts +5 -3
  198. package/dist/packages/{engine → runtime}/build/index.js +5 -3
  199. package/dist/packages/{engine → runtime}/build/lib/schema.d.ts +221 -88
  200. package/dist/packages/{engine → runtime}/build/lib/schema.js +61 -41
  201. package/dist/packages/runtime/build/reset.d.ts +2 -0
  202. package/dist/packages/runtime/build/reset.js +62 -0
  203. package/dist/packages/{engine → runtime}/build/runtime-contracts.js +5 -5
  204. package/dist/packages/runtime/build/runtime-inventory.d.ts +7 -0
  205. package/dist/packages/{engine → runtime}/build/runtime-inventory.js +3 -3
  206. package/dist/packages/{engine → runtime}/build/runtime-paths.js +6 -6
  207. package/dist/packages/{engine → runtime}/build/runtime-prompt.js +7 -6
  208. package/dist/packages/{engine → runtime}/build/runtime-reconcile.d.ts +1 -1
  209. package/dist/packages/{engine → runtime}/build/runtime-reconcile.js +25 -21
  210. package/dist/packages/runtime/build/runtime-runs.d.ts +10 -0
  211. package/dist/packages/runtime/build/runtime-runs.js +265 -0
  212. package/dist/packages/{engine → runtime}/build/runtime-types.d.ts +6 -6
  213. package/dist/packages/runtime/build/runtime-types.js +1 -0
  214. package/dist/packages/runtime/build/runtime.d.ts +6 -0
  215. package/dist/packages/runtime/build/runtime.js +5 -0
  216. package/dist/packages/runtime/build/source-files.d.ts +58 -0
  217. package/dist/packages/runtime/build/source-files.js +184 -0
  218. package/dist/packages/runtime/build/source-inventory.d.ts +27 -0
  219. package/dist/packages/runtime/build/source-inventory.js +273 -0
  220. package/dist/packages/runtime/build/source-manifest.d.ts +52 -0
  221. package/dist/packages/runtime/build/source-manifest.js +192 -0
  222. package/dist/packages/runtime/build/stage-evidence.d.ts +22 -0
  223. package/dist/packages/runtime/build/stage-evidence.js +317 -0
  224. package/dist/packages/runtime/build/stage-reuse.d.ts +11 -0
  225. package/dist/packages/runtime/build/stage-reuse.js +142 -0
  226. package/dist/packages/runtime/build/state-artifacts.d.ts +9 -0
  227. package/dist/packages/runtime/build/state-artifacts.js +14 -0
  228. package/dist/packages/runtime/build/state-health.d.ts +4 -0
  229. package/dist/packages/{engine → runtime}/build/state-health.js +21 -26
  230. package/dist/packages/runtime/build/state-io.d.ts +12 -0
  231. package/dist/packages/runtime/build/state-io.js +119 -0
  232. package/dist/packages/{engine → runtime}/build/state-paths.d.ts +3 -1
  233. package/dist/packages/runtime/build/state-paths.js +22 -0
  234. package/dist/packages/runtime/build/state-view.d.ts +5 -0
  235. package/dist/packages/{engine → runtime}/build/state-view.js +60 -33
  236. package/dist/packages/runtime/build/state.d.ts +7 -0
  237. package/dist/packages/runtime/build/state.js +12 -0
  238. package/dist/packages/runtime/build/traces.d.ts +30 -0
  239. package/dist/packages/runtime/build/traces.js +133 -0
  240. package/dist/packages/{engine/build/validate-verifiable-context.d.ts → runtime/build/validate-context-graph.d.ts} +6 -6
  241. package/dist/packages/{engine/build/validate-verifiable-context.js → runtime/build/validate-context-graph.js} +48 -35
  242. package/dist/packages/{engine → runtime}/build/validate.d.ts +5 -5
  243. package/dist/packages/{engine → runtime}/build/validate.js +26 -26
  244. package/dist/packages/{engine → runtime}/client.d.ts +15 -15
  245. package/dist/packages/{engine → runtime}/client.js +41 -24
  246. package/dist/packages/{engine → runtime}/connection-config.d.ts +3 -2
  247. package/dist/packages/{engine → runtime}/connection-config.js +9 -8
  248. package/dist/packages/runtime/context-checks.d.ts +10 -0
  249. package/dist/packages/runtime/context-checks.js +114 -0
  250. package/dist/packages/runtime/context-graph-scaffold.d.ts +9 -0
  251. package/dist/packages/runtime/context-graph-scaffold.js +134 -0
  252. package/dist/packages/runtime/entitlement-guard.d.ts +43 -0
  253. package/dist/packages/runtime/entitlement-guard.js +70 -0
  254. package/dist/packages/{engine → runtime}/execution/index.d.ts +2 -2
  255. package/dist/packages/{engine → runtime}/execution/index.js +1 -1
  256. package/dist/packages/{engine → runtime}/execution/lib/schema.d.ts +270 -192
  257. package/dist/packages/{engine → runtime}/execution/lib/schema.js +35 -32
  258. package/dist/packages/runtime/index.d.ts +29 -0
  259. package/dist/packages/runtime/index.js +21 -0
  260. package/dist/packages/runtime/instance-paths.d.ts +30 -0
  261. package/dist/packages/runtime/instance-paths.js +29 -0
  262. package/dist/packages/{engine → runtime}/native-run-handlers.d.ts +4 -4
  263. package/dist/packages/{engine → runtime}/native-run-handlers.js +184 -152
  264. package/dist/packages/runtime/plan-artifact-contract.d.ts +17 -0
  265. package/dist/packages/runtime/plan-artifact-contract.js +42 -0
  266. package/dist/packages/runtime/project-entries.d.ts +11 -0
  267. package/dist/packages/runtime/project-entries.js +49 -0
  268. package/dist/packages/runtime/project-source-state.d.ts +26 -0
  269. package/dist/packages/runtime/project-source-state.js +53 -0
  270. package/dist/packages/runtime/project-store.d.ts +85 -0
  271. package/dist/packages/runtime/project-store.js +168 -0
  272. package/dist/packages/runtime/requested-artifacts.d.ts +7 -0
  273. package/dist/packages/{engine → runtime}/requested-artifacts.js +23 -1
  274. package/dist/packages/{engine → runtime}/run-observability.d.ts +2 -1
  275. package/dist/packages/{engine → runtime}/run-observability.js +166 -84
  276. package/dist/packages/runtime/runtime-action-proposals.d.ts +7 -0
  277. package/dist/packages/runtime/runtime-action-proposals.js +542 -0
  278. package/dist/packages/runtime/runtime-build-plans.d.ts +5 -0
  279. package/dist/packages/runtime/runtime-build-plans.js +131 -0
  280. package/dist/packages/runtime/runtime-build-runs.d.ts +47 -0
  281. package/dist/packages/runtime/runtime-build-runs.js +562 -0
  282. package/dist/packages/runtime/runtime-caches.d.ts +91 -0
  283. package/dist/packages/{engine → runtime}/runtime-caches.js +77 -49
  284. package/dist/packages/{engine → runtime}/runtime-event-applier.d.ts +3 -1
  285. package/dist/packages/{engine → runtime}/runtime-event-applier.js +53 -17
  286. package/dist/packages/runtime/runtime-executor.d.ts +22 -0
  287. package/dist/packages/runtime/runtime-executor.js +131 -0
  288. package/dist/packages/runtime/runtime-jobs.d.ts +13 -0
  289. package/dist/packages/runtime/runtime-jobs.js +463 -0
  290. package/dist/packages/runtime/runtime-observability.d.ts +11 -0
  291. package/dist/packages/runtime/runtime-observability.js +39 -0
  292. package/dist/packages/{engine → runtime}/runtime-persistence.d.ts +9 -18
  293. package/dist/packages/{engine → runtime}/runtime-persistence.js +25 -25
  294. package/dist/packages/runtime/runtime-project-mutations.d.ts +7 -0
  295. package/dist/packages/runtime/runtime-project-mutations.js +64 -0
  296. package/dist/packages/runtime/runtime-project-reads.d.ts +15 -0
  297. package/dist/packages/runtime/runtime-project-reads.js +381 -0
  298. package/dist/packages/runtime/runtime-proposal-helpers.d.ts +22 -0
  299. package/dist/packages/runtime/runtime-proposal-helpers.js +223 -0
  300. package/dist/packages/{engine → runtime}/runtime-resource-builders.d.ts +21 -16
  301. package/dist/packages/{engine → runtime}/runtime-resource-builders.js +48 -38
  302. package/dist/packages/runtime/runtime-verify-runs.d.ts +84 -0
  303. package/dist/packages/runtime/runtime-verify-runs.js +295 -0
  304. package/dist/packages/runtime/runtime.d.ts +1165 -0
  305. package/dist/packages/runtime/runtime.js +417 -0
  306. package/dist/packages/runtime/schemas/actions.d.ts +1182 -0
  307. package/dist/packages/runtime/schemas/actions.js +117 -0
  308. package/dist/packages/runtime/schemas/agents.d.ts +76 -0
  309. package/dist/packages/runtime/schemas/agents.js +41 -0
  310. package/dist/packages/runtime/schemas/build-plans.d.ts +959 -0
  311. package/dist/packages/runtime/schemas/build-plans.js +107 -0
  312. package/dist/packages/runtime/schemas/files.d.ts +567 -0
  313. package/dist/packages/runtime/schemas/files.js +51 -0
  314. package/dist/packages/runtime/schemas/index.d.ts +8 -0
  315. package/dist/packages/runtime/schemas/index.js +12 -0
  316. package/dist/packages/runtime/schemas/instance.d.ts +141 -0
  317. package/dist/packages/runtime/schemas/instance.js +143 -0
  318. package/dist/packages/runtime/schemas/jobs.d.ts +339 -0
  319. package/dist/packages/runtime/schemas/jobs.js +103 -0
  320. package/dist/packages/runtime/schemas/projects.d.ts +339 -0
  321. package/dist/packages/runtime/schemas/projects.js +136 -0
  322. package/dist/packages/runtime/schemas/runs.d.ts +2676 -0
  323. package/dist/packages/runtime/schemas/runs.js +98 -0
  324. package/dist/packages/runtime/service/index.d.ts +3 -0
  325. package/dist/packages/runtime/service/index.js +3 -0
  326. package/dist/packages/runtime/service/openapi.d.ts +7 -0
  327. package/dist/packages/runtime/service/openapi.js +117 -0
  328. package/dist/packages/runtime/service/operations.d.ts +1490 -0
  329. package/dist/packages/runtime/service/operations.js +245 -0
  330. package/dist/packages/runtime/service/routes.d.ts +106 -0
  331. package/dist/packages/runtime/service/routes.js +120 -0
  332. package/dist/packages/runtime/service/server-api-files.d.ts +10 -0
  333. package/dist/packages/runtime/service/server-api-files.js +85 -0
  334. package/dist/packages/runtime/service/server-app-boot.d.ts +4 -0
  335. package/dist/packages/runtime/service/server-app-boot.js +46 -0
  336. package/dist/packages/runtime/service/server-guards.d.ts +63 -0
  337. package/dist/packages/runtime/service/server-guards.js +181 -0
  338. package/dist/packages/runtime/service/server-helpers.d.ts +27 -0
  339. package/dist/packages/runtime/service/server-helpers.js +89 -0
  340. package/dist/packages/runtime/service/server-instance-helpers.d.ts +30 -0
  341. package/dist/packages/runtime/service/server-instance-helpers.js +114 -0
  342. package/dist/packages/runtime/service/server-routes-action-proposals.d.ts +3 -0
  343. package/dist/packages/runtime/service/server-routes-action-proposals.js +43 -0
  344. package/dist/packages/runtime/service/server-routes-agents.d.ts +4 -0
  345. package/dist/packages/runtime/service/server-routes-agents.js +198 -0
  346. package/dist/packages/runtime/service/server-routes-auth.d.ts +33 -0
  347. package/dist/packages/runtime/service/server-routes-auth.js +138 -0
  348. package/dist/packages/runtime/service/server-routes-build-plans.d.ts +3 -0
  349. package/dist/packages/runtime/service/server-routes-build-plans.js +83 -0
  350. package/dist/packages/runtime/service/server-routes-discovery.d.ts +4 -0
  351. package/dist/packages/runtime/service/server-routes-discovery.js +196 -0
  352. package/dist/packages/runtime/service/server-routes-events.d.ts +5 -0
  353. package/dist/packages/runtime/service/server-routes-events.js +99 -0
  354. package/dist/packages/runtime/service/server-routes-project-context.d.ts +9 -0
  355. package/dist/packages/runtime/service/server-routes-project-context.js +192 -0
  356. package/dist/packages/runtime/service/server-routes-project-jobs.d.ts +9 -0
  357. package/dist/packages/runtime/service/server-routes-project-jobs.js +130 -0
  358. package/dist/packages/runtime/service/server-routes-project-runs.d.ts +14 -0
  359. package/dist/packages/runtime/service/server-routes-project-runs.js +85 -0
  360. package/dist/packages/runtime/service/server-routes-projects.d.ts +4 -0
  361. package/dist/packages/runtime/service/server-routes-projects.js +92 -0
  362. package/dist/packages/runtime/service/server-routes-runs.d.ts +3 -0
  363. package/dist/packages/runtime/service/server-routes-runs.js +112 -0
  364. package/dist/packages/runtime/service/server.d.ts +37 -0
  365. package/dist/packages/runtime/service/server.js +300 -0
  366. package/dist/packages/{engine → runtime/service}/service-registry.d.ts +5 -5
  367. package/dist/packages/{engine → runtime/service}/service-registry.js +7 -7
  368. package/dist/packages/runtime/verify/benchmark-run.d.ts +81 -0
  369. package/dist/packages/runtime/verify/benchmark-run.js +303 -0
  370. package/dist/packages/{engine → runtime}/verify/index.d.ts +2 -2
  371. package/dist/packages/{engine → runtime}/verify/index.js +1 -1
  372. package/dist/packages/{engine → runtime}/verify/lib/schema.d.ts +83 -16
  373. package/dist/packages/{engine → runtime}/verify/lib/schema.js +26 -18
  374. package/dist/packages/{engine → runtime}/verify/verify-execution.js +11 -11
  375. package/dist/packages/{engine → runtime}/verify/verify-paths.d.ts +4 -4
  376. package/dist/packages/runtime/verify/verify-paths.js +61 -0
  377. package/dist/packages/{engine → runtime}/verify/verify-sandbox.d.ts +1 -1
  378. package/dist/packages/runtime/verify/verify-sandbox.js +88 -0
  379. package/dist/packages/{engine → runtime}/verify/verify-specs.d.ts +2 -0
  380. package/dist/packages/{engine → runtime}/verify/verify-specs.js +5 -3
  381. package/dist/packages/runtime/verify/verify-targets.d.ts +5 -0
  382. package/dist/packages/{engine → runtime}/verify/verify-targets.js +12 -12
  383. package/dist/packages/runtime/verify/verify-types.js +1 -0
  384. package/dist/packages/{engine → runtime}/verify/verify.d.ts +1 -1
  385. package/dist/packages/{engine → runtime}/verify/verify.js +1 -1
  386. package/dist/packages/runtime/wire-schemas.d.ts +18 -0
  387. package/dist/packages/runtime/wire-schemas.js +27 -0
  388. package/package.json +40 -30
  389. package/public-repo/CONTRIBUTING.md +7 -16
  390. package/public-repo/README.md +175 -234
  391. package/public-repo/SECURITY.md +3 -4
  392. package/public-repo/build-plans/interf-default/README.md +19 -14
  393. package/public-repo/build-plans/interf-default/build/stages/shape/SKILL.md +18 -11
  394. package/public-repo/build-plans/interf-default/build/stages/structure/SKILL.md +12 -5
  395. package/public-repo/build-plans/interf-default/build/stages/summarize/SKILL.md +16 -4
  396. package/public-repo/build-plans/interf-default/build-plan.json +34 -28
  397. package/public-repo/build-plans/interf-default/build-plan.schema.json +54 -32
  398. package/public-repo/build-plans/interf-default/improve/SKILL.md +3 -3
  399. package/public-repo/build-plans/interf-default/use/query/SKILL.md +17 -11
  400. package/public-repo/openapi/local-service.openapi.json +6803 -0
  401. package/public-repo/skills/interf/SKILL.md +460 -179
  402. package/dist/cli/commands/prep.d.ts +0 -2
  403. package/dist/cli/commands/prep.js +0 -240
  404. package/dist/cli/commands/test.d.ts +0 -10
  405. package/dist/cli/commands/test.js +0 -85
  406. package/dist/cli/commands/web.d.ts +0 -2
  407. package/dist/cli/commands/web.js +0 -286
  408. package/dist/interf-ui/404.html +0 -1
  409. package/dist/interf-ui/__next.__PAGE__.txt +0 -10
  410. package/dist/interf-ui/__next._full.txt +0 -20
  411. package/dist/interf-ui/__next._head.txt +0 -5
  412. package/dist/interf-ui/__next._index.txt +0 -5
  413. package/dist/interf-ui/__next._tree.txt +0 -5
  414. package/dist/interf-ui/_next/static/_qJKnA0dammQ306MG_zMq/_buildManifest.js +0 -11
  415. package/dist/interf-ui/_next/static/_qJKnA0dammQ306MG_zMq/_clientMiddlewareManifest.js +0 -1
  416. package/dist/interf-ui/_next/static/_qJKnA0dammQ306MG_zMq/_ssgManifest.js +0 -1
  417. package/dist/interf-ui/_next/static/chunks/0.tjb6f4golw..css +0 -3
  418. package/dist/interf-ui/_next/static/chunks/03~yq9q893hmn.js +0 -1
  419. package/dist/interf-ui/_next/static/chunks/085-n_jv2ng_q.css +0 -1
  420. package/dist/interf-ui/_next/static/chunks/0dn41fa_zvgsl.js +0 -1
  421. package/dist/interf-ui/_next/static/chunks/0g-ea0zj5d-0k.js +0 -1
  422. package/dist/interf-ui/_next/static/chunks/0gwqglc4iz583.js +0 -1
  423. package/dist/interf-ui/_next/static/chunks/0haldgm65ve6l.js +0 -1
  424. package/dist/interf-ui/_next/static/chunks/0nv3am99vjzn4.js +0 -1
  425. package/dist/interf-ui/_next/static/chunks/0s77gt_o4jwtx.js +0 -1
  426. package/dist/interf-ui/_next/static/chunks/0y5z3t-z1c8ks.js.map +0 -5
  427. package/dist/interf-ui/_next/static/chunks/0~a36ujuzpaz..js +0 -116
  428. package/dist/interf-ui/_next/static/chunks/10jeodxe4nkgj.js +0 -31
  429. package/dist/interf-ui/_next/static/chunks/119h2rouych2t.js +0 -1
  430. package/dist/interf-ui/_next/static/chunks/13c8b~m8knjsf.js +0 -1
  431. package/dist/interf-ui/_next/static/chunks/14dznb2qpt-ho.js +0 -91
  432. package/dist/interf-ui/_next/static/chunks/15z_en80lrq-3.js +0 -5
  433. package/dist/interf-ui/_next/static/chunks/turbopack-0p.pvcjrtq-jh.js +0 -1
  434. package/dist/interf-ui/_next/static/chunks/turbopack-0usj_75.8frlw.js +0 -1
  435. package/dist/interf-ui/_next/static/chunks/turbopack-worker-0sjn--fhq~1cg.js +0 -1
  436. package/dist/interf-ui/_next/static/media/GeistMono_Variable.p.17jn9btb_52pq.woff2 +0 -0
  437. package/dist/interf-ui/_next/static/media/Geist_Variable-s.p.0-te~ja_gpvcf.woff2 +0 -0
  438. package/dist/interf-ui/_next/static/media/worker.102zas1s52_pf.js +0 -109
  439. package/dist/interf-ui/_not-found/__next._full.txt +0 -15
  440. package/dist/interf-ui/_not-found/__next._head.txt +0 -5
  441. package/dist/interf-ui/_not-found/__next._index.txt +0 -5
  442. package/dist/interf-ui/_not-found/__next._not-found.__PAGE__.txt +0 -5
  443. package/dist/interf-ui/_not-found/__next._not-found.txt +0 -5
  444. package/dist/interf-ui/_not-found/__next._tree.txt +0 -2
  445. package/dist/interf-ui/_not-found.html +0 -1
  446. package/dist/interf-ui/_not-found.txt +0 -15
  447. package/dist/interf-ui/index.html +0 -1
  448. package/dist/interf-ui/index.txt +0 -20
  449. package/dist/packages/contracts/lib/preparation-paths.d.ts +0 -117
  450. package/dist/packages/contracts/lib/preparation-paths.js +0 -177
  451. package/dist/packages/engine/action-definitions.d.ts +0 -407
  452. package/dist/packages/engine/action-definitions.js +0 -1158
  453. package/dist/packages/engine/action-values.d.ts +0 -1
  454. package/dist/packages/engine/action-values.js +0 -1
  455. package/dist/packages/engine/agents/lib/args.d.ts +0 -4
  456. package/dist/packages/engine/agents/lib/args.js +0 -52
  457. package/dist/packages/engine/agents/lib/chart-guidance.d.ts +0 -1
  458. package/dist/packages/engine/agents/lib/chart-guidance.js +0 -8
  459. package/dist/packages/engine/agents/lib/constants.js +0 -28
  460. package/dist/packages/engine/agents/lib/schema.d.ts +0 -8
  461. package/dist/packages/engine/agents/lib/schema.js +0 -7
  462. package/dist/packages/engine/agents/lib/shells.d.ts +0 -74
  463. package/dist/packages/engine/agents/lib/shells.js +0 -1052
  464. package/dist/packages/engine/agents/lib/verifiable-context-bootstrap.d.ts +0 -3
  465. package/dist/packages/engine/build/artifact-counts.d.ts +0 -1
  466. package/dist/packages/engine/build/billing-events.d.ts +0 -89
  467. package/dist/packages/engine/build/build-pipeline.js +0 -175
  468. package/dist/packages/engine/build/build-plan-runs.d.ts +0 -14
  469. package/dist/packages/engine/build/build-plan-runs.js +0 -31
  470. package/dist/packages/engine/build/build-stage-plan.d.ts +0 -16
  471. package/dist/packages/engine/build/build-stage-runner.js +0 -94
  472. package/dist/packages/engine/build/build-target.js +0 -16
  473. package/dist/packages/engine/build/check-evaluator.js +0 -298
  474. package/dist/packages/engine/build/reset.d.ts +0 -2
  475. package/dist/packages/engine/build/reset.js +0 -74
  476. package/dist/packages/engine/build/runtime-inventory.d.ts +0 -7
  477. package/dist/packages/engine/build/runtime-runs.d.ts +0 -10
  478. package/dist/packages/engine/build/runtime-runs.js +0 -224
  479. package/dist/packages/engine/build/runtime.d.ts +0 -5
  480. package/dist/packages/engine/build/runtime.js +0 -4
  481. package/dist/packages/engine/build/source-files.d.ts +0 -46
  482. package/dist/packages/engine/build/source-files.js +0 -149
  483. package/dist/packages/engine/build/state-artifacts.d.ts +0 -9
  484. package/dist/packages/engine/build/state-artifacts.js +0 -14
  485. package/dist/packages/engine/build/state-health.d.ts +0 -4
  486. package/dist/packages/engine/build/state-io.d.ts +0 -11
  487. package/dist/packages/engine/build/state-io.js +0 -82
  488. package/dist/packages/engine/build/state-paths.js +0 -16
  489. package/dist/packages/engine/build/state-view.d.ts +0 -5
  490. package/dist/packages/engine/build/state.d.ts +0 -7
  491. package/dist/packages/engine/build/state.js +0 -12
  492. package/dist/packages/engine/build/validate-helpers.d.ts +0 -12
  493. package/dist/packages/engine/build/validate-helpers.js +0 -41
  494. package/dist/packages/engine/build/verifiable-context-paths.d.ts +0 -47
  495. package/dist/packages/engine/build/verifiable-context-paths.js +0 -121
  496. package/dist/packages/engine/build/verifiable-context-schema.d.ts +0 -21
  497. package/dist/packages/engine/build/verifiable-context-schema.js +0 -126
  498. package/dist/packages/engine/cloud-seams.d.ts +0 -115
  499. package/dist/packages/engine/cloud-seams.js +0 -84
  500. package/dist/packages/engine/index.d.ts +0 -22
  501. package/dist/packages/engine/index.js +0 -15
  502. package/dist/packages/engine/instance-paths.d.ts +0 -106
  503. package/dist/packages/engine/instance-paths.js +0 -171
  504. package/dist/packages/engine/lib/schema.d.ts +0 -6304
  505. package/dist/packages/engine/lib/schema.js +0 -730
  506. package/dist/packages/engine/preparation-store.d.ts +0 -105
  507. package/dist/packages/engine/preparation-store.js +0 -213
  508. package/dist/packages/engine/readiness-check-draft.d.ts +0 -20
  509. package/dist/packages/engine/readiness-check-draft.js +0 -111
  510. package/dist/packages/engine/requested-artifacts.d.ts +0 -5
  511. package/dist/packages/engine/routes.d.ts +0 -85
  512. package/dist/packages/engine/routes.js +0 -99
  513. package/dist/packages/engine/runtime-caches.d.ts +0 -76
  514. package/dist/packages/engine/runtime-proposal-helpers.d.ts +0 -35
  515. package/dist/packages/engine/runtime-proposal-helpers.js +0 -247
  516. package/dist/packages/engine/runtime.d.ts +0 -371
  517. package/dist/packages/engine/runtime.js +0 -2463
  518. package/dist/packages/engine/server.d.ts +0 -58
  519. package/dist/packages/engine/server.js +0 -1399
  520. package/dist/packages/engine/verify/readiness-check-run.d.ts +0 -82
  521. package/dist/packages/engine/verify/readiness-check-run.js +0 -265
  522. package/dist/packages/engine/verify/verify-paths.js +0 -61
  523. package/dist/packages/engine/verify/verify-sandbox.js +0 -88
  524. package/dist/packages/engine/verify/verify-targets.d.ts +0 -5
  525. package/dist/packages/engine/wire-schemas.d.ts +0 -547
  526. package/dist/packages/engine/wire-schemas.js +0 -59
  527. package/dist/packages/project/index.d.ts +0 -9
  528. package/dist/packages/project/interf-bootstrap.d.ts +0 -1
  529. package/dist/packages/project/interf-bootstrap.js +0 -1
  530. package/dist/packages/project/interf-scaffold.d.ts +0 -3
  531. package/dist/packages/project/interf-scaffold.js +0 -136
  532. package/dist/packages/project/interf.d.ts +0 -4
  533. package/dist/packages/project/interf.js +0 -3
  534. package/dist/packages/project/lib/schema.d.ts +0 -328
  535. package/dist/packages/project/lib/schema.js +0 -136
  536. package/dist/packages/project/preparation-entries.d.ts +0 -11
  537. package/dist/packages/project/preparation-entries.js +0 -49
  538. package/dist/packages/project/source-config.d.ts +0 -46
  539. package/dist/packages/project/source-config.js +0 -394
  540. package/dist/packages/project/source-folders.d.ts +0 -11
  541. package/public-repo/plugins/README.md +0 -9
  542. package/public-repo/plugins/interf/.claude-plugin/plugin.json +0 -21
  543. package/public-repo/plugins/interf/.mcp.json +0 -12
  544. package/public-repo/plugins/interf/README.md +0 -32
  545. package/public-repo/plugins/interf/skills/interf/SKILL.md +0 -376
  546. /package/dist/packages/{engine/agents/lib/types.js → runtime/actions/fields.js} +0 -0
  547. /package/dist/packages/{engine → runtime}/agents/lib/agents.d.ts +0 -0
  548. /package/dist/packages/{engine → runtime}/agents/lib/agents.js +0 -0
  549. /package/dist/packages/{engine → runtime}/agents/lib/execution-profile.d.ts +0 -0
  550. /package/dist/packages/{engine → runtime}/agents/lib/execution.d.ts +0 -0
  551. /package/dist/packages/{engine → runtime}/agents/lib/executors.d.ts +0 -0
  552. /package/dist/packages/{engine → runtime}/agents/lib/executors.js +0 -0
  553. /package/dist/packages/{engine → runtime}/agents/lib/logs.d.ts +0 -0
  554. /package/dist/packages/{engine → runtime}/agents/lib/logs.js +0 -0
  555. /package/dist/packages/{engine → runtime}/agents/lib/preflight.d.ts +0 -0
  556. /package/dist/packages/{engine → runtime}/agents/lib/render.d.ts +0 -0
  557. /package/dist/packages/{engine → runtime}/agents/lib/status.d.ts +0 -0
  558. /package/dist/packages/{engine → runtime}/agents/lib/status.js +0 -0
  559. /package/dist/packages/{engine → runtime}/agents/lib/types.d.ts +0 -0
  560. /package/dist/packages/{engine/build/runtime-types.js → runtime/agents/lib/types.js} +0 -0
  561. /package/dist/packages/{engine/verify/verify-types.js → runtime/agents/providers/types.js} +0 -0
  562. /package/dist/packages/{engine → runtime}/agents/role-router.d.ts +0 -0
  563. /package/dist/packages/{engine → runtime}/build/build-execution.d.ts +0 -0
  564. /package/dist/packages/{engine → runtime}/build/build-execution.js +0 -0
  565. /package/dist/packages/{engine → runtime}/build/runtime-contracts.d.ts +0 -0
  566. /package/dist/packages/{engine → runtime}/build/runtime-paths.d.ts +0 -0
  567. /package/dist/packages/{engine → runtime}/build/runtime-prompt.d.ts +0 -0
  568. /package/dist/packages/{engine → runtime}/execution/adapters.d.ts +0 -0
  569. /package/dist/packages/{engine → runtime}/execution/adapters.js +0 -0
  570. /package/dist/packages/{engine → runtime}/execution/events.d.ts +0 -0
  571. /package/dist/packages/{engine → runtime}/execution/events.js +0 -0
  572. /package/dist/packages/{engine → runtime}/verify/verify-execution.d.ts +0 -0
  573. /package/dist/packages/{engine → runtime}/verify/verify-profile-presets.d.ts +0 -0
  574. /package/dist/packages/{engine → runtime}/verify/verify-profile-presets.js +0 -0
  575. /package/dist/packages/{engine → runtime}/verify/verify-types.d.ts +0 -0
@@ -1,2463 +0,0 @@
1
- import { existsSync, mkdirSync, rmSync, statSync, } from "node:fs";
2
- import { join, resolve } from "node:path";
3
- import { BuildRunSchema, } from "./execution/lib/schema.js";
4
- import { createRunEventId, createRunEventTimestamp, } from "./execution/events.js";
5
- import { loadState, } from "./build/state.js";
6
- import { actionProposalPath, actionProposalsRoot, byCreatedAtDesc, buildRunPath, buildRunsRoot, listJsonFiles, localJobPath, localJobsRoot, newestFirst, readActionProposalAt, readBuildRunAt, readLocalJobRunAt, readRuntimeRunHistory, readVerifyRunAt, verifyRunPath, verifyRunsRoot, timestampKey, writeJsonFile, } from "./runtime-persistence.js";
7
- import { BuildPlanListingCache, MtimeListingCache, ReadinessCache, RunListingCache, } from "./runtime-caches.js";
8
- import { applyEventToBuildRun, applyEventToLocalJob, } from "./runtime-event-applier.js";
9
- import { buildBuildPlanResource, buildPreparationResource, createRunId, logsForRuntimeRun, logsForStageRun, proofForStage, readinessStateToPreparationReadiness, readinessSummaryForStatus, readinessTargetResult, stageArtifactRefs, } from "./runtime-resource-builders.js";
10
- import { ACTION_PLANNER_CLARIFICATION_MESSAGE, actionAssistantMessage, actionCommandPreview, actionTypeFromValues, actionValueBuildPlanTaskPrompt, configuredAgentName, createActionProposalId, detachBuildPlanFromPreparation, detectedExecutorOptions, directServiceEndpointForAction, hasVerifiableContextTestTarget, buildPlanAuthoringHintFromPrompt, buildPlanAuthoringPromptFallback, buildPlanIdForProposal, buildPlanLabelFromId, numberValue, requireSelectedBuildPlan, sanitizeActionProposalPlan, stringValue, testModeFromValues, } from "./runtime-proposal-helpers.js";
11
- import { ReadinessSchema, } from "../contracts/lib/schema.js";
12
- import { discoverSourceFiles, } from "./build/discovery.js";
13
- import { resetBuildGeneratedState, } from "./build/reset.js";
14
- import { ensureVerifiableContextScaffold, readInterfConfig, } from "../project/interf.js";
15
- import { findSourcePreparationConfig, fingerprintReadinessChecks, listSourcePreparationConfigs, loadSourceFolderConfig, DEFAULT_BUILD_PLAN_ID, buildPlanIdForSourcePreparationConfig, resolveConfiguredSourceFolderPath, resolveSourcePreparationPath, removeSourcePreparationConfig, saveSourceFolderConfig, syncVerifiableContextInterfConfigFromSourcePreparationConfig, upsertSourcePreparationConfig, } from "../project/source-config.js";
16
- import { listSourceFolderChoices, } from "../project/source-folders.js";
17
- import { asPreparationDataDir, preparationVerifiableContextPath, userBuildPlansRoot, preparationConfigPath, preparationBuildPlanPackagePath, preparationBuildPlansRoot, } from "../contracts/lib/preparation-paths.js";
18
- import { getBuildPlan, listBuildPlanChoices, } from "../build-plans/package/build-plan-definitions.js";
19
- import { aggregateArtifactVerdict, computeArtifactStatuses, } from "./build/artifact-status.js";
20
- import { JsonlBillingEventSink, buildCompilationEventsForRun, defaultBillingEventLogPath, } from "./build/billing-events.js";
21
- import { buildPlanDefinitionPath, resolveBuildPlanPackageSourcePath, } from "../build-plans/package/local-build-plans.js";
22
- import { seedLocalBuildPlanPackageFromBase, } from "../build-plans/package/interf-build-plan-package.js";
23
- import { PACKAGE_ROOT } from "../build-plans/package/lib/package-root.js";
24
- import { resolveAgent, detectAgents, supportsAutomatedRuns, } from "./agents/lib/detection.js";
25
- import { loadUserConfig, saveUserConfig, } from "./agents/lib/user-config.js";
26
- import { loadAgentsRegistry, registerCustomAgent, unregisterCustomAgent, patchRoleMap, setActiveAgent, } from "./agents/registry.js";
27
- import { readSavedReadinessCheckRun, } from "./verify/readiness-check-run.js";
28
- import { createVerifiableContextTestTarget, } from "./verify/verify-targets.js";
29
- import { ActionProposalApprovalRequestSchema, ActionProposalCreateRequestSchema, ActionProposalPlanSchema, ActionProposalResourceSchema, ActionProposalTypeSchema, BuildRunCreateRequestSchema, BuildRunResourceSchema, LocalExecutorStatusSchema, LocalExecutorSelectRequestSchema, LocalServiceHealthSchema, LocalRunHandlerResultSchema, LocalJobEventAppendRequestSchema, LocalJobRunCreateRequestSchema, LocalJobRunResourceSchema, SourceFileResourceSchema, VerifiableContextResourceSchema, PreparationSetupCreateRequestSchema, PreparationSetupResultSchema, BuildPlanChangeCreateRequestSchema, BuildPlanChangeResultSchema, PreparationChangeCreateRequestSchema, PreparationChangeResultSchema, ReadinessCheckDraftCreateRequestSchema, ReadinessCheckDraftResultSchema, ResetRequestSchema, ResetResultSchema, VerifyRunCreateRequestSchema, VerifyRunResourceSchema, BuildPlanAuthoringCreateRequestSchema, BuildPlanAuthoringResultSchema, } from "./lib/schema.js";
30
- import { buildLocalServiceUrl, } from "./routes.js";
31
- import { BuildPlanAuthoringActionValuesSchema, PreparationSetupActionValuesSchema, } from "./action-values.js";
32
- import { buildRunToObservability, jobRunToObservability, verifyRunToObservability, uniqueArtifacts, } from "./run-observability.js";
33
- import { artifactRequirementsFromRequestedArtifacts } from "./requested-artifacts.js";
34
- /** TTL for `POS../../build-runs` idempotency-key dedupe entries. */
35
- const IDEMPOTENCY_TTL_MS = 60 * 60 * 1000;
36
- /** Idempotency cache size at which to schedule an opportunistic prune. */
37
- const IDEMPOTENCY_PRUNE_THRESHOLD = 64;
38
- const INTERRUPTED_COMPILE_RUN_MESSAGE = "Build run interrupted because the Interf engine stopped before the run reached a terminal state.";
39
- const INTERRUPTED_JOB_RUN_MESSAGE = "Job interrupted because the Interf engine stopped before the job reached a terminal state.";
40
- function isTerminalBuildRunStatus(status) {
41
- return status === "succeeded" || status === "failed" || status === "cancelled";
42
- }
43
- function isTerminalJobStatus(status) {
44
- return status === "succeeded" || status === "failed" || status === "cancelled";
45
- }
46
- function countsFromVerifiableContextState(state) {
47
- const counts = {};
48
- for (const stage of Object.values(state.stages ?? {})) {
49
- for (const [key, value] of Object.entries(stage.counts ?? {})) {
50
- if (Number.isFinite(value))
51
- counts[key] = Math.max(counts[key] ?? 0, value);
52
- }
53
- for (const [key, value] of Object.entries(stage.artifact_counts ?? {})) {
54
- if (Number.isFinite(value))
55
- counts[key] = Math.max(counts[key] ?? 0, value);
56
- }
57
- }
58
- return counts;
59
- }
60
- export class LocalServiceRuntime {
61
- host;
62
- port;
63
- startedAt;
64
- packageVersion;
65
- handlers;
66
- /**
67
- * The seed root path the runtime was constructed with. Used as a
68
- * non-preparation fallback when a preparation-independent route
69
- * (Build Plans, action proposals, runs listings) needs an anchor to load
70
- * shared state (user-library Build Plans, bundled Build Plans, etc).
71
- */
72
- rootPath;
73
- /**
74
- * Per-instance bearer token. Mutating routes require this on the
75
- * Authorization header. `null` means token-less mode (test harness).
76
- */
77
- authToken;
78
- /** Map of prepDataDir -> PreparationContext. */
79
- preparationContexts = new Map();
80
- /** Hook called whenever a preparation is registered or deregistered. */
81
- onRegistryChanged = null;
82
- /** In-flight runs across all preparations. Used for `idle_for_seconds`. */
83
- activeRunCount = 0;
84
- /**
85
- * Active Build run cancellation handles, keyed by run id. Populated
86
- * when a Build run is launched and cleared once the run reaches a
87
- * terminal state. Each entry remembers where the persisted record lives
88
- * so cancel can mark it without re-resolving the Preparation.
89
- *
90
- * TODO(cloud): when the cloud variant lands, wire `RunLeaseStore`
91
- * (see `cloud-seams.ts` B4.2) through this map. Multi-replica engines
92
- * need a shared lease store so a replica can take over a run whose
93
- * owning replica died.
94
- */
95
- activeBuildRuns = new Map();
96
- /**
97
- * Idempotency-key cache for `POS../../build-runs`. Outer key is the
98
- * resolved preparation root; inner key is the client-supplied idempotency
99
- * value. Namespacing per preparation prevents key collisions across
100
- * tenants on the same engine (CSO finding: a malicious preparation could
101
- * otherwise hijack another preparation's run id by reusing its key).
102
- * Entries expire after `IDEMPOTENCY_TTL_MS`.
103
- *
104
- * TODO(cloud): when the cloud variant lands, wire `IdempotencyStore`
105
- * (see `cloud-seams.ts` B4.1) through this map. Multi-replica engines
106
- * need a shared store so retries that land on a different replica
107
- * still hit the same dedupe entry.
108
- */
109
- idempotencyKeyCache = new Map();
110
- /**
111
- * Read-side caches. Polling clients (Interf UI, CLI status loops)
112
- * hit list/get endpoints multiple times per second; without these,
113
- * every request re-walks the filesystem and re-parses every JSON
114
- * record through Zod. The runtime invalidates each cache on the
115
- * matching write path. See {@link runtime-caches} for design notes.
116
- */
117
- buildRunCache = new RunListingCache();
118
- verifyRunCache = new RunListingCache();
119
- readinessCache = new ReadinessCache();
120
- sourceFilesCache = new MtimeListingCache();
121
- buildPlanListingCache = new BuildPlanListingCache();
122
- /**
123
- * 0.17 — sink for per-Artifact billing events. Set once at construction.
124
- * Lazy-defaults to a per-run JSONL writer when no override was injected.
125
- */
126
- billingEventSink;
127
- /**
128
- * 0.17 — cloud-variant injection points. The local engine accepts
129
- * these on the options surface but does not consume them yet — the
130
- * in-process Maps remain authoritative for idempotency and
131
- * run-lease state. The cloud variant fork will swap to these stores;
132
- * the option-fields are documented here so the cloud build has a
133
- * stable target. See `cloud-seams.ts` for the contracts.
134
- */
135
- cloudIdempotencyStore;
136
- cloudRunLeaseStore;
137
- cloudTokenValidator = null;
138
- constructor(options) {
139
- this.host = options.host;
140
- this.port = options.port;
141
- this.startedAt = options.startedAt ?? new Date().toISOString();
142
- this.packageVersion = options.packageVersion;
143
- this.handlers = options.handlers ?? {};
144
- this.authToken = options.authToken ?? null;
145
- this.rootPath = resolve(options.rootPath);
146
- this.billingEventSink = options.billingEventSink ?? null;
147
- this.cloudIdempotencyStore = options.idempotencyStore ?? null;
148
- this.cloudRunLeaseStore = options.runLeaseStore ?? null;
149
- // Auto-register the initial preparation so single-preparation callers
150
- // (existing tests, the current `interf web` command) work without
151
- // additional bootstrapping. The constructor seed is the only role
152
- // `options.rootPath` plays; runtime operations take `prepDataDir`
153
- // explicitly afterwards.
154
- this.registerPreparation(this.rootPath);
155
- }
156
- setBoundPort(port) {
157
- this.port = port;
158
- }
159
- /**
160
- * 0.17 — token validator setter for cloud variants. Stored on the
161
- * runtime so `isAuthorizedMutation` in `server.ts` can opt into the
162
- * async per-account check when present. Local default: never set —
163
- * the static bearer-token check runs.
164
- */
165
- setTokenValidator(validator) {
166
- this.cloudTokenValidator = validator;
167
- }
168
- getTokenValidator() {
169
- return this.cloudTokenValidator;
170
- }
171
- /** Set a hook that fires whenever the registered preparations change. */
172
- setOnRegistryChanged(handler) {
173
- this.onRegistryChanged = handler;
174
- }
175
- /**
176
- * Register a preparation with this runtime. Returns the PreparationContext.
177
- * Idempotent: re-registering an existing preparation updates `lastActivity`.
178
- */
179
- registerPreparation(prepDataDir) {
180
- const resolved = resolve(prepDataDir);
181
- const now = new Date().toISOString();
182
- const existing = this.preparationContexts.get(resolved);
183
- if (existing) {
184
- existing.lastActivity = now;
185
- this.onRegistryChanged?.();
186
- return existing;
187
- }
188
- const context = {
189
- rootPath: resolved,
190
- startedAt: now,
191
- lastActivity: now,
192
- };
193
- this.preparationContexts.set(resolved, context);
194
- this.finalizeInterruptedBuildRuns(resolved);
195
- this.finalizeInterruptedJobRuns(resolved);
196
- this.onRegistryChanged?.();
197
- return context;
198
- }
199
- /**
200
- * Remove a preparation from the runtime. Returns true if a preparation was
201
- * removed.
202
- */
203
- deregisterPreparation(prepDataDir) {
204
- const resolved = resolve(prepDataDir);
205
- const removed = this.preparationContexts.delete(resolved);
206
- if (removed) {
207
- this.onRegistryChanged?.();
208
- }
209
- return removed;
210
- }
211
- /**
212
- * Most recently active preparation, or the first registered if none has
213
- * activity yet. Server code uses this as the fallback when a request
214
- * does not specify a preparation. Throws if none are registered.
215
- */
216
- defaultPreparationDataDir() {
217
- if (this.preparationContexts.size === 0) {
218
- throw new Error("Local service has no registered preparations.");
219
- }
220
- let best = null;
221
- let bestKey = -Infinity;
222
- for (const context of this.preparationContexts.values()) {
223
- const key = Date.parse(context.lastActivity);
224
- if (Number.isFinite(key) && key > bestKey) {
225
- best = context;
226
- bestKey = key;
227
- }
228
- }
229
- return (best ?? this.preparationContexts.values().next().value).rootPath;
230
- }
231
- /** Look up a preparation context by rootPath. */
232
- getPreparationContext(prepDataDir) {
233
- return this.preparationContexts.get(resolve(prepDataDir)) ?? null;
234
- }
235
- /** All registered preparations, ordered by registration time. */
236
- listRegisteredPreparations() {
237
- return Array.from(this.preparationContexts.values()).sort((left, right) => Date.parse(left.startedAt) - Date.parse(right.startedAt));
238
- }
239
- /** True when no preparations are registered. */
240
- hasNoPreparations() {
241
- return this.preparationContexts.size === 0;
242
- }
243
- /** Number of registered preparations. */
244
- registeredPreparationCount() {
245
- return this.preparationContexts.size;
246
- }
247
- /** Increment in-flight run counter. Call when a long-running run starts. */
248
- beginActiveRun() {
249
- this.activeRunCount += 1;
250
- }
251
- /** Decrement in-flight run counter. Pair with `beginActiveRun`. */
252
- endActiveRun() {
253
- if (this.activeRunCount > 0)
254
- this.activeRunCount -= 1;
255
- }
256
- /** Sum of in-flight runs across all preparations. */
257
- activeRuns() {
258
- return this.activeRunCount;
259
- }
260
- /**
261
- * Mark the preparation as recently active. Routes call this on entry so
262
- * `idleForSeconds` and the registry snapshots stay in sync with the
263
- * actual request cadence.
264
- */
265
- touchPreparation(prepDataDir) {
266
- const context = this.preparationContexts.get(resolve(prepDataDir));
267
- if (context) {
268
- context.lastActivity = new Date().toISOString();
269
- }
270
- }
271
- /** Seconds since the most recent preparation activity (0 if active). */
272
- idleForSeconds() {
273
- const all = this.listRegisteredPreparations();
274
- if (all.length === 0)
275
- return 0;
276
- if (this.activeRunCount > 0)
277
- return 0;
278
- let mostRecent = 0;
279
- for (const context of all) {
280
- const ts = Date.parse(context.lastActivity);
281
- if (Number.isFinite(ts) && ts > mostRecent)
282
- mostRecent = ts;
283
- }
284
- if (mostRecent <= 0)
285
- return 0;
286
- const elapsed = Math.max(0, Date.now() - mostRecent);
287
- return Math.floor(elapsed / 1000);
288
- }
289
- health(prepDataDir) {
290
- const sourceFolderPath = prepDataDir ? resolveConfiguredSourceFolderPath(prepDataDir) : null;
291
- return LocalServiceHealthSchema.parse({
292
- kind: "interf-local-service-health",
293
- version: 1,
294
- status: "ok",
295
- host: this.host,
296
- port: this.port,
297
- service_url: buildLocalServiceUrl({ host: this.host, port: this.port }),
298
- ...(prepDataDir ? { control_path: prepDataDir } : {}),
299
- source_folder_path: sourceFolderPath,
300
- started_at: this.startedAt,
301
- ...(this.packageVersion ? { package_version: this.packageVersion } : {}),
302
- instance_started_at: this.startedAt,
303
- active_runs: this.activeRunCount,
304
- idle_for_seconds: this.idleForSeconds(),
305
- });
306
- }
307
- listPreparations(prepDataDir) {
308
- const config = loadSourceFolderConfig(prepDataDir);
309
- return listSourcePreparationConfigs(config).map((preparation) => {
310
- const buildRuns = this.listBuildRunsForPreparation(prepDataDir, preparation.name);
311
- const verifyRuns = this.listVerifyRunsForPreparation(prepDataDir, preparation.name);
312
- const readiness = this.computePreparationReadiness(prepDataDir, preparation);
313
- return buildPreparationResource(prepDataDir, preparation, readiness, buildRuns[0]?.run_id ?? null, verifyRuns[0]?.run_id ?? null,
314
- // 0.17 — surface per-Artifact status from the latest build
315
- // run so the UI can render artifact rows on the Preparation
316
- // page without a separate fetch.
317
- buildRuns[0]?.artifacts ?? []);
318
- });
319
- }
320
- getPreparation(prepDataDir, preparationName) {
321
- return this.listPreparations(prepDataDir).find((preparation) => preparation.name === preparationName) ?? null;
322
- }
323
- listPreparationReadiness(prepDataDir) {
324
- return this.listReadiness(prepDataDir).map(readinessStateToPreparationReadiness);
325
- }
326
- getPreparationReadiness(prepDataDir, preparationName) {
327
- const readiness = this.getReadiness(prepDataDir, preparationName);
328
- return readiness ? readinessStateToPreparationReadiness(readiness) : null;
329
- }
330
- listReadiness(prepDataDir) {
331
- const config = loadSourceFolderConfig(prepDataDir);
332
- return listSourcePreparationConfigs(config).map((preparation) => this.computePreparationReadiness(prepDataDir, preparation));
333
- }
334
- getReadiness(prepDataDir, preparationName) {
335
- const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), preparationName);
336
- return preparation ? this.computePreparationReadiness(prepDataDir, preparation) : null;
337
- }
338
- computePreparationReadiness(prepDataDir, preparation) {
339
- return this.readinessCache.get(prepDataDir, preparation.name, () => this.computePreparationReadinessUncached(prepDataDir, preparation));
340
- }
341
- computePreparationReadinessUncached(prepDataDir, preparation) {
342
- const generatedAt = new Date().toISOString();
343
- const verifiableContextPath = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
344
- const contextExists = existsSync(verifiableContextPath);
345
- const verifiableContextTarget = createVerifiableContextTestTarget(verifiableContextPath, preparation.name, buildPlanIdForSourcePreparationConfig(preparation) ?? DEFAULT_BUILD_PLAN_ID);
346
- const contextReady = verifiableContextTarget.eligible;
347
- const buildRun = this.listBuildRunsForPreparation(prepDataDir, preparation.name)[0] ?? null;
348
- const verifyRun = this.listVerifyRunsForPreparation(prepDataDir, preparation.name)[0] ?? null;
349
- const readinessRun = this.readLatestReadinessRun(prepDataDir, preparation.name);
350
- const artifactStatuses = buildRun?.artifacts ?? [];
351
- const hasArtifactContract = artifactStatuses.length > 0;
352
- const artifactVerdict = aggregateArtifactVerdict(artifactStatuses);
353
- const artifactFailures = artifactStatuses.filter((artifact) => artifact.status !== "ready");
354
- const artifactProofs = artifactStatuses.flatMap((artifact) => artifact.proofs ?? []);
355
- const requiredArtifactProofs = artifactProofs.filter((proof) => proof.required !== false);
356
- const passedArtifactProofs = requiredArtifactProofs.filter((proof) => proof.passed);
357
- const configuredChecks = preparation.checks.length;
358
- const currentFingerprint = configuredChecks > 0 ? fingerprintReadinessChecks(preparation.checks) : null;
359
- const readinessRunFingerprint = readinessRun?.checks_fingerprint ?? null;
360
- const sourceResult = readinessTargetResult(readinessRun?.source_files, currentFingerprint, readinessRunFingerprint);
361
- const contextResult = readinessTargetResult(readinessRun?.verifiable_context, currentFingerprint, readinessRunFingerprint);
362
- const checksStale = Boolean(currentFingerprint && readinessRunFingerprint && currentFingerprint !== readinessRunFingerprint);
363
- const buildCheck = (() => {
364
- if (!buildRun) {
365
- return {
366
- gate: "build-run",
367
- ok: false,
368
- status: "not-built",
369
- summary: "No Build run has built verifiable context yet.",
370
- artifact_path: contextReady ? verifiableContextPath : null,
371
- };
372
- }
373
- if (buildRun.status === "succeeded") {
374
- return {
375
- gate: "build-run",
376
- ok: contextReady,
377
- status: contextReady ? "built" : "failed",
378
- summary: contextReady
379
- ? "Latest Build run built verifiable context."
380
- : "Latest Build run finished, but verifiable context is missing.",
381
- run_id: buildRun.run_id,
382
- artifact_path: contextReady ? verifiableContextPath : null,
383
- };
384
- }
385
- if (buildRun.status === "queued" || buildRun.status === "running") {
386
- return {
387
- gate: "build-run",
388
- ok: false,
389
- status: "building",
390
- summary: "Latest Build run is still building verifiable context.",
391
- run_id: buildRun.run_id,
392
- artifact_path: contextExists ? verifiableContextPath : null,
393
- };
394
- }
395
- return {
396
- gate: "build-run",
397
- ok: false,
398
- status: "failed",
399
- summary: buildRun.status === "cancelled"
400
- ? "Latest Build run was cancelled."
401
- : "Latest Build run failed.",
402
- run_id: buildRun.run_id,
403
- artifact_path: contextReady ? verifiableContextPath : null,
404
- };
405
- })();
406
- const checks = [
407
- {
408
- gate: "preparation-config",
409
- ok: true,
410
- summary: "Preparation is saved in this control plane folder.",
411
- },
412
- {
413
- gate: "verifiable-context",
414
- ok: contextReady,
415
- status: contextReady ? "built" : "not-built",
416
- summary: contextReady
417
- ? "Verifiable context is built."
418
- : "Verifiable context has not been built.",
419
- artifact_path: contextReady ? verifiableContextPath : null,
420
- },
421
- buildCheck,
422
- {
423
- gate: "artifact-checks",
424
- ok: !hasArtifactContract || artifactVerdict === "ready",
425
- status: !hasArtifactContract
426
- ? "not-configured"
427
- : artifactVerdict === "ready" ? "ready" : "not-ready",
428
- summary: !hasArtifactContract
429
- ? "No Artifacts are declared by the selected Build Plan."
430
- : artifactVerdict === "ready"
431
- ? `${artifactStatuses.length} Artifact${artifactStatuses.length === 1 ? "" : "s"} ready; ${passedArtifactProofs.length}/${requiredArtifactProofs.length} required Artifact check${requiredArtifactProofs.length === 1 ? "" : "s"} passed.`
432
- : `${artifactFailures.length} Artifact${artifactFailures.length === 1 ? "" : "s"} not ready.`,
433
- },
434
- {
435
- gate: "readiness-checks",
436
- ok: true,
437
- status: configuredChecks > 0 ? "built" : "not-configured",
438
- summary: configuredChecks > 0
439
- ? `${configuredChecks} readiness check${configuredChecks === 1 ? "" : "s"} configured.`
440
- : "No optional readiness checks are configured.",
441
- },
442
- {
443
- gate: "checks-current",
444
- ok: !checksStale,
445
- status: checksStale ? "stale" : "built",
446
- summary: checksStale
447
- ? "Latest readiness result is stale for the current saved checks."
448
- : "Latest readiness result matches the current saved checks.",
449
- },
450
- ];
451
- const status = (() => {
452
- if (buildRun?.status === "queued" || buildRun?.status === "running")
453
- return "building";
454
- if (verifyRun?.status === "queued" || verifyRun?.status === "running")
455
- return "checking";
456
- if (buildRun?.status === "failed" || buildRun?.status === "cancelled")
457
- return "failed";
458
- if (!buildRun || !contextReady)
459
- return "not-built";
460
- if (hasArtifactContract && artifactVerdict !== "ready")
461
- return "not-ready";
462
- if (checksStale)
463
- return "stale";
464
- if (configuredChecks === 0)
465
- return hasArtifactContract ? "ready" : "built";
466
- if (!contextResult)
467
- return "built";
468
- return contextResult.total > 0 && contextResult.passed === contextResult.total ? "ready" : "not-ready";
469
- })();
470
- const ready = status === "ready";
471
- return ReadinessSchema.parse({
472
- kind: "interf-readiness-state",
473
- version: 1,
474
- generated_at: generatedAt,
475
- preparation: preparation.name,
476
- status,
477
- ready,
478
- summary: readinessSummaryForStatus(status),
479
- verifiable_context_path: contextReady ? verifiableContextPath : null,
480
- latest_build_run_id: buildRun?.run_id ?? null,
481
- latest_test_run_id: verifyRun?.run_id ?? null,
482
- build: buildCheck,
483
- check_results: {
484
- configured: configuredChecks,
485
- fingerprint: currentFingerprint,
486
- source_files: sourceResult,
487
- verifiable_context: contextResult,
488
- },
489
- checks,
490
- });
491
- }
492
- listSourceFiles(prepDataDir, preparationName) {
493
- const config = loadSourceFolderConfig(prepDataDir);
494
- const preparations = listSourcePreparationConfigs(config)
495
- .filter((preparation) => !preparationName || preparation.name === preparationName);
496
- // 0.13+ source binding: the source folder is the user-supplied
497
- // absolute path on `source_folder.path`. `preparation.path` is the
498
- // verifiable-context name inside `prepDataDir` (legacy field; in the
499
- // current Preparation layout it equals `preparation.name`). Walk
500
- // the actual source bytes, not the verifiable-context subdir.
501
- const sourceFolderPath = resolveConfiguredSourceFolderPath(prepDataDir, config) ?? prepDataDir;
502
- return preparations.flatMap((preparation) => {
503
- const verifiableContextPath = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
504
- // Cache by source-folder root mtime so identical UI polls do not
505
- // re-walk and re-stat the entire tree. The cache imposes a short
506
- // TTL (see runtime-caches.ts) so deeper changes are still picked
507
- // up promptly.
508
- const cacheKey = `${preparation.name}\0${sourceFolderPath}\0${verifiableContextPath}`;
509
- return this.sourceFilesCache.get(cacheKey, sourceFolderPath, () => {
510
- return discoverSourceFiles(sourceFolderPath, verifiableContextPath).sourceFiles.map((relativePath) => {
511
- const absolutePath = join(sourceFolderPath, relativePath);
512
- let sizeBytes = 0;
513
- let modifiedAt = null;
514
- try {
515
- const stat = statSync(absolutePath);
516
- sizeBytes = stat.size;
517
- modifiedAt = stat.mtime.toISOString();
518
- }
519
- catch {
520
- sizeBytes = 0;
521
- modifiedAt = null;
522
- }
523
- return SourceFileResourceSchema.parse({
524
- preparation: preparation.name,
525
- path: relativePath,
526
- absolute_path: absolutePath,
527
- size_bytes: sizeBytes,
528
- modified_at: modifiedAt,
529
- source_folder_path: sourceFolderPath,
530
- });
531
- });
532
- });
533
- });
534
- }
535
- listBuildPlans(prepDataDir) {
536
- // The Build Plan choices list is dominated by repeated reads of
537
- // build-plan.json + context-interface across bundled, user-library, and
538
- // Preparation-local Build Plan package roots. Key the cache off mtimes; if
539
- // any of them changes (a new local Build Plan, an edit to the user
540
- // library, etc.) the cache misses and we re-resolve.
541
- const builtinRoot = join(PACKAGE_ROOT, "public-repo", "build-plans");
542
- const localRoot = preparationBuildPlansRoot(asPreparationDataDir(prepDataDir));
543
- const userRoot = userBuildPlansRoot();
544
- return this.buildPlanListingCache.get(prepDataDir, [builtinRoot, localRoot, userRoot], () => {
545
- const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
546
- const choices = listBuildPlanChoices(prepDataDir);
547
- return choices.map((buildPlan) => {
548
- const activeForPreparations = preparations
549
- .filter((preparation) => buildPlanIdForSourcePreparationConfig(preparation) === buildPlan.id)
550
- .map((preparation) => preparation.name);
551
- return buildBuildPlanResource({
552
- id: buildPlan.id,
553
- path: resolveBuildPlanPackageSourcePath(prepDataDir, buildPlan.id) ?? buildPlan.id,
554
- label: buildPlan.label,
555
- hint: buildPlan.hint,
556
- purpose: buildPlan.purpose,
557
- inputs: buildPlan.inputs,
558
- source_kind: buildPlan.scope === "builtin" ? "builtin" : "local",
559
- built_in: buildPlan.scope === "builtin",
560
- active_for_preparations: activeForPreparations,
561
- artifacts: buildPlan.contextInterface?.artifacts ?? [],
562
- stages: buildPlan.stages.map((stage) => ({
563
- id: stage.id,
564
- label: stage.label,
565
- description: stage.description,
566
- contract_type: stage.contractType,
567
- skill_dir: stage.skillDir,
568
- role: stage.role && stage.role.trim().length > 0 ? stage.role : "general",
569
- reads: stage.reads,
570
- writes: stage.writes,
571
- })),
572
- });
573
- });
574
- });
575
- }
576
- getBuildPlanResource(prepDataDir, buildPlanId) {
577
- return this.listBuildPlans(prepDataDir).find((buildPlan) => buildPlan.id === buildPlanId) ?? null;
578
- }
579
- listJobs(prepDataDir) {
580
- return byCreatedAtDesc(listJsonFiles(localJobsRoot(prepDataDir))
581
- .map(readLocalJobRunAt)
582
- .filter((run) => run !== null));
583
- }
584
- getJob(prepDataDir, runId) {
585
- return this.listJobs(prepDataDir).find((run) => run.run_id === runId) ?? null;
586
- }
587
- getJobEvents(prepDataDir, runId) {
588
- return this.getJob(prepDataDir, runId)?.events ?? null;
589
- }
590
- getExecutorStatus() {
591
- const checkedAt = new Date().toISOString();
592
- try {
593
- const resolved = resolveAgent();
594
- const currentName = resolved.agent?.name ?? configuredAgentName();
595
- const availableExecutors = detectedExecutorOptions(currentName);
596
- if (!resolved.agent) {
597
- return LocalExecutorStatusSchema.parse({
598
- kind: "interf-local-executor-status",
599
- version: 1,
600
- status: "unavailable",
601
- checked_at: checkedAt,
602
- executor: null,
603
- available_executors: availableExecutors,
604
- message: resolved.error ?? "No supported local coding agent is configured.",
605
- });
606
- }
607
- return LocalExecutorStatusSchema.parse({
608
- kind: "interf-local-executor-status",
609
- version: 1,
610
- status: "connected",
611
- checked_at: checkedAt,
612
- executor: {
613
- name: resolved.agent.name,
614
- display_name: resolved.agent.displayName,
615
- command: resolved.agent.command,
616
- },
617
- available_executors: availableExecutors.length > 0
618
- ? availableExecutors
619
- : [{
620
- name: resolved.agent.name,
621
- display_name: resolved.agent.displayName,
622
- command: resolved.agent.command,
623
- current: true,
624
- }],
625
- message: `${resolved.agent.displayName} is configured for local Interf actions.`,
626
- });
627
- }
628
- catch (error) {
629
- const currentName = configuredAgentName();
630
- return LocalExecutorStatusSchema.parse({
631
- kind: "interf-local-executor-status",
632
- version: 1,
633
- status: "unavailable",
634
- checked_at: checkedAt,
635
- executor: null,
636
- available_executors: detectedExecutorOptions(currentName),
637
- message: error instanceof Error ? error.message : String(error),
638
- });
639
- }
640
- }
641
- selectExecutor(requestValue) {
642
- const request = LocalExecutorSelectRequestSchema.parse(requestValue);
643
- const selected = detectAgents()
644
- .filter(supportsAutomatedRuns)
645
- .find((agent) => agent.name === request.agent);
646
- if (!selected) {
647
- throw new Error(`Local agent "${request.agent}" is not detected or cannot run Interf runs.`);
648
- }
649
- const current = loadUserConfig();
650
- saveUserConfig({
651
- agent: selected.name,
652
- agentCommand: selected.command,
653
- skillsInstalled: current?.skillsInstalled ?? false,
654
- initialized: current?.initialized ?? new Date().toISOString(),
655
- });
656
- // Mirror the selection into the 0.15 role-map. `setActiveAgent`
657
- // also sweeps any role still pointing at the previous active so
658
- // single-active-agent setups stay coherent across CLIs.
659
- try {
660
- setActiveAgent(selected.name);
661
- }
662
- catch {
663
- // Best effort — the registry update is convenience for the new
664
- // role-aware path. The user_config.json write above is the
665
- // source of truth for the legacy `getExecutorStatus()`.
666
- }
667
- return this.getExecutorStatus();
668
- }
669
- // ─── 0.15 connected-agents primitive ─────────────────────────────
670
- /**
671
- * Snapshot of the merged agents registry (built-in detected + custom)
672
- * with the current role-map and resolved active agent.
673
- */
674
- getAgentsRegistry() {
675
- const registry = loadAgentsRegistry();
676
- return {
677
- agents: registry.agents,
678
- role_map: registry.roleMap,
679
- active_agent: registry.activeAgent,
680
- };
681
- }
682
- registerCustomAgent(input) {
683
- registerCustomAgent(input);
684
- return this.getAgentsRegistry();
685
- }
686
- unregisterCustomAgent(name) {
687
- unregisterCustomAgent(name);
688
- return this.getAgentsRegistry();
689
- }
690
- patchAgentsRoleMap(patch) {
691
- patchRoleMap(patch);
692
- const registry = loadAgentsRegistry();
693
- return {
694
- role_map: registry.roleMap,
695
- active_agent: registry.activeAgent,
696
- };
697
- }
698
- listActionProposals(prepDataDir) {
699
- return byCreatedAtDesc(listJsonFiles(actionProposalsRoot(prepDataDir))
700
- .map(readActionProposalAt)
701
- .filter((proposal) => proposal !== null));
702
- }
703
- getActionProposal(prepDataDir, proposalId) {
704
- return this.listActionProposals(prepDataDir).find((proposal) => proposal.proposal_id === proposalId) ?? null;
705
- }
706
- async createActionProposal(prepDataDir, requestValue) {
707
- const request = ActionProposalCreateRequestSchema.parse(requestValue);
708
- const proposal = ActionProposalResourceSchema.parse({
709
- ...(await this.buildActionProposal(prepDataDir, request)),
710
- client_origin: request.client_origin,
711
- });
712
- this.writeActionProposal(prepDataDir, proposal);
713
- return proposal;
714
- }
715
- async decideActionProposal(prepDataDir, proposalId, requestValue) {
716
- const decision = ActionProposalApprovalRequestSchema.parse(requestValue);
717
- const current = this.getActionProposal(prepDataDir, proposalId);
718
- if (!current)
719
- return null;
720
- if (current.status !== "awaiting_approval") {
721
- throw new Error(`Action proposal "${proposalId}" is already ${current.status}.`);
722
- }
723
- const now = new Date().toISOString();
724
- const decided = ActionProposalResourceSchema.parse({
725
- ...current,
726
- status: decision.approved ? "approved" : "denied",
727
- updated_at: now,
728
- approval: {
729
- approved: decision.approved,
730
- decided_at: now,
731
- ...(decision.note ? { note: decision.note } : {}),
732
- },
733
- });
734
- this.writeActionProposal(prepDataDir, decided);
735
- if (!decision.approved)
736
- return decided;
737
- try {
738
- const submission = await this.submitActionProposal(prepDataDir, decided);
739
- const submitted = ActionProposalResourceSchema.parse({
740
- ...decided,
741
- status: "submitted",
742
- updated_at: new Date().toISOString(),
743
- submitted_run_id: submission.runId,
744
- submitted_run_type: submission.runType,
745
- });
746
- this.writeActionProposal(prepDataDir, submitted);
747
- return submitted;
748
- }
749
- catch (error) {
750
- const failed = ActionProposalResourceSchema.parse({
751
- ...decided,
752
- status: "failed",
753
- updated_at: new Date().toISOString(),
754
- error: error instanceof Error ? error.message : String(error),
755
- });
756
- this.writeActionProposal(prepDataDir, failed);
757
- return failed;
758
- }
759
- }
760
- listRunObservability(prepDataDir) {
761
- return [
762
- ...this.listBuildRuns(prepDataDir).map((resource) => buildRunToObservability({
763
- ...resource.run,
764
- readiness: this.getReadiness(prepDataDir, resource.run.preparation),
765
- })),
766
- ...this.listVerifyRuns(prepDataDir).map(verifyRunToObservability),
767
- ...this.listJobs(prepDataDir).map(jobRunToObservability),
768
- ].sort((left, right) => {
769
- const leftTime = timestampKey(left.started_at ?? left.created_at ?? left.finished_at);
770
- const rightTime = timestampKey(right.started_at ?? right.created_at ?? right.finished_at);
771
- return rightTime - leftTime;
772
- });
773
- }
774
- getRunObservability(prepDataDir, runId) {
775
- return this.listRunObservability(prepDataDir).find((run) => run.run_id === runId) ?? null;
776
- }
777
- /**
778
- * Build Plan Activity runs: every Build Plan draft or improvement job whose
779
- * Build Plan id matches `buildPlanId`. Surfaced through
780
- * `GET /v1/build-plans/<id>/runs` so Build Plan detail can show the full
781
- * audit trail of authoring + improvement work.
782
- */
783
- listBuildPlanRuns(prepDataDir, buildPlanId) {
784
- return this.listRunObservability(prepDataDir).filter((run) => {
785
- if (run.build_plan !== buildPlanId)
786
- return false;
787
- return (run.run_type === "build-plan-draft" ||
788
- run.run_type === "build-plan-improvement");
789
- });
790
- }
791
- createJobRun(prepDataDir, requestValue) {
792
- const request = LocalJobRunCreateRequestSchema.parse(requestValue);
793
- const runId = createRunId("job");
794
- const now = new Date().toISOString();
795
- const run = LocalJobRunResourceSchema.parse({
796
- kind: "interf-local-job-run",
797
- version: 1,
798
- run_id: runId,
799
- job_type: request.job_type,
800
- status: "running",
801
- title: request.title,
802
- ...(request.preparation !== undefined ? { preparation: request.preparation } : {}),
803
- ...(request.build_plan !== undefined ? { build_plan: request.build_plan } : {}),
804
- ...(request.source_path !== undefined ? { source_path: request.source_path } : {}),
805
- ...(request.output_path !== undefined ? { output_path: request.output_path } : {}),
806
- ...(request.agent !== undefined ? { agent: request.agent } : {}),
807
- created_at: now,
808
- started_at: now,
809
- steps: request.steps.map((step) => ({
810
- ...step,
811
- status: "queued",
812
- })),
813
- events: [
814
- {
815
- type: "job.started",
816
- event_id: createRunEventId("event"),
817
- run_id: runId,
818
- timestamp: now,
819
- message: request.title,
820
- },
821
- ],
822
- });
823
- this.writeJobRun(prepDataDir, run);
824
- return run;
825
- }
826
- appendJobRunEvent(prepDataDir, runId, requestValue) {
827
- const request = LocalJobEventAppendRequestSchema.parse(requestValue);
828
- const current = this.getJob(prepDataDir, runId);
829
- if (!current)
830
- return null;
831
- const event = {
832
- type: request.type,
833
- event_id: createRunEventId("event"),
834
- run_id: runId,
835
- timestamp: new Date().toISOString(),
836
- ...(request.step_id ? { step_id: request.step_id } : {}),
837
- ...(request.message ? { message: request.message } : {}),
838
- ...(request.artifact ? { artifact: request.artifact } : {}),
839
- ...(request.input ? { input: request.input } : {}),
840
- ...(request.output ? { output: request.output } : {}),
841
- };
842
- const next = LocalJobRunResourceSchema.parse(applyEventToLocalJob(current, event));
843
- this.writeJobRun(prepDataDir, next);
844
- return next;
845
- }
846
- async createReadinessCheckDraftRun(prepDataDir, requestValue) {
847
- const request = ReadinessCheckDraftCreateRequestSchema.parse(requestValue);
848
- const job = this.createJobRun(prepDataDir, {
849
- job_type: "readiness-check-draft",
850
- title: `Draft readiness checks for ${request.preparation}`,
851
- preparation: request.preparation,
852
- source_path: request.source_folder_path,
853
- steps: [
854
- {
855
- id: "read-source",
856
- label: "Read source files",
857
- input: {
858
- preparation: request.preparation,
859
- source_folder_path: request.source_folder_path,
860
- },
861
- },
862
- {
863
- id: "agent-draft",
864
- label: "Draft readiness checks",
865
- input: {
866
- about: request.about ?? null,
867
- target_count: request.target_count,
868
- },
869
- },
870
- {
871
- id: "normalize-checks",
872
- label: "Normalize saved readiness checks",
873
- },
874
- ],
875
- });
876
- this.appendJobRunEvent(prepDataDir, job.run_id, {
877
- type: "step.started",
878
- step_id: "read-source",
879
- message: "Reading source files for readiness-check evidence.",
880
- input: {
881
- preparation: request.preparation,
882
- source_folder_path: request.source_folder_path,
883
- },
884
- });
885
- this.appendJobRunEvent(prepDataDir, job.run_id, {
886
- type: "step.completed",
887
- step_id: "read-source",
888
- message: "Source folder is ready for drafting readiness checks.",
889
- output: {
890
- preparation: request.preparation,
891
- source_folder_path: request.source_folder_path,
892
- },
893
- });
894
- this.appendJobRunEvent(prepDataDir, job.run_id, {
895
- type: "step.started",
896
- step_id: "agent-draft",
897
- message: "Drafting saved readiness checks from the source files.",
898
- input: {
899
- about: request.about ?? null,
900
- target_count: request.target_count,
901
- },
902
- });
903
- void this.runReadinessCheckDraftInBackground(prepDataDir, request, job.run_id);
904
- return this.getJob(prepDataDir, job.run_id) ?? job;
905
- }
906
- applyBuildPlanChange(prepDataDir, requestValue) {
907
- const request = BuildPlanChangeCreateRequestSchema.parse(requestValue);
908
- const buildPlanId = request.build_plan;
909
- const outputPath = request.operation === "duplicate"
910
- ? buildPlanDefinitionPath(prepDataDir, request.new_build_plan_id)
911
- : buildPlanDefinitionPath(prepDataDir, buildPlanId);
912
- if (request.operation === "duplicate") {
913
- if (resolveBuildPlanPackageSourcePath(prepDataDir, request.new_build_plan_id)) {
914
- throw new Error(`Build Plan "${request.new_build_plan_id}" already exists.`);
915
- }
916
- if (!resolveBuildPlanPackageSourcePath(prepDataDir, buildPlanId)) {
917
- throw new Error(`Build Plan "${buildPlanId}" does not exist.`);
918
- }
919
- const label = request.label ?? buildPlanLabelFromId(request.new_build_plan_id);
920
- const hint = request.hint ?? `Duplicate of ${buildPlanId}`;
921
- const buildPlanPath = seedLocalBuildPlanPackageFromBase({
922
- prepDataDir,
923
- baseBuildPlanId: buildPlanId,
924
- buildPlanId: request.new_build_plan_id,
925
- label,
926
- hint,
927
- });
928
- this.buildPlanListingCache.invalidate(prepDataDir);
929
- return BuildPlanChangeResultSchema.parse({
930
- kind: "interf-build-plan-change-result",
931
- version: 1,
932
- operation: "duplicate",
933
- build_plan: buildPlanId,
934
- new_build_plan_id: request.new_build_plan_id,
935
- updated_preparations: [],
936
- build_plan_path: buildPlanPath,
937
- changed: true,
938
- message: `Duplicated Build Plan ${buildPlanId} as ${request.new_build_plan_id}.`,
939
- });
940
- }
941
- if (request.confirmation !== buildPlanId) {
942
- throw new Error(`Type ${buildPlanId} to confirm Build Plan removal.`);
943
- }
944
- const localBuildPlanPath = buildPlanDefinitionPath(prepDataDir, buildPlanId);
945
- if (buildPlanId === DEFAULT_BUILD_PLAN_ID || !existsSync(localBuildPlanPath)) {
946
- throw new Error(`Build Plan "${buildPlanId}" is not a removable local Build Plan.`);
947
- }
948
- const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
949
- const updatedPreparations = preparations
950
- .filter((preparation) => buildPlanIdForSourcePreparationConfig(preparation) === buildPlanId);
951
- if (updatedPreparations.length > 0) {
952
- saveSourceFolderConfig(prepDataDir, {
953
- preparations: preparations.map((preparation) => detachBuildPlanFromPreparation(preparation, buildPlanId)),
954
- });
955
- // Detaching a Build Plan changes readiness shape for those Preparations.
956
- for (const preparation of updatedPreparations) {
957
- this.readinessCache.invalidatePreparation(prepDataDir, preparation.name);
958
- }
959
- }
960
- rmSync(outputPath, { recursive: true, force: true });
961
- this.buildPlanListingCache.invalidate(prepDataDir);
962
- return BuildPlanChangeResultSchema.parse({
963
- kind: "interf-build-plan-change-result",
964
- version: 1,
965
- operation: "remove",
966
- build_plan: buildPlanId,
967
- updated_preparations: updatedPreparations.map((preparation) => preparation.name),
968
- build_plan_path: outputPath,
969
- changed: true,
970
- message: updatedPreparations.length > 0
971
- ? `Removed Build Plan ${buildPlanId} and cleared it from ${updatedPreparations.length} Preparation(s).`
972
- : `Removed Build Plan ${buildPlanId}.`,
973
- });
974
- }
975
- applyPreparationSetup(prepDataDir, requestValue) {
976
- const request = PreparationSetupCreateRequestSchema.parse(requestValue);
977
- const preparationConfig = request.preparation;
978
- const buildPlanId = buildPlanIdForSourcePreparationConfig(preparationConfig);
979
- if (request.setup_mode === "select-build-plan" && !buildPlanId) {
980
- throw new Error("Build Plan is required when selecting a Build Plan for a Preparation.");
981
- }
982
- const normalizedPreparationConfig = buildPlanId
983
- ? { ...preparationConfig, build_plan: buildPlanId }
984
- : (() => {
985
- const { build_plan: _ignoredBuildPlan, ...withoutBuildPlan } = preparationConfig;
986
- return withoutBuildPlan;
987
- })();
988
- const sourceFolderPath = resolveSourcePreparationPath(prepDataDir, normalizedPreparationConfig);
989
- if (!existsSync(sourceFolderPath) || !statSync(sourceFolderPath).isDirectory()) {
990
- throw new Error(`Source folder "${preparationConfig.path}" is not available.`);
991
- }
992
- upsertSourcePreparationConfig(prepDataDir, normalizedPreparationConfig);
993
- // The Preparation's bound source folder + Build Plan may have changed:
994
- // bust the per-preparation readiness, runs, and Build Plan listing
995
- // caches so the next read reflects the new shape.
996
- this.readinessCache.invalidatePreparation(prepDataDir, normalizedPreparationConfig.name);
997
- this.buildRunCache.invalidatePreparation(prepDataDir, normalizedPreparationConfig.name);
998
- this.verifyRunCache.invalidatePreparation(prepDataDir, normalizedPreparationConfig.name);
999
- this.buildPlanListingCache.invalidate(prepDataDir);
1000
- const operation = request.setup_mode === "select-build-plan" ? "select-build-plan" : "create";
1001
- return PreparationSetupResultSchema.parse({
1002
- kind: "interf-preparation-setup-result",
1003
- version: 1,
1004
- operation,
1005
- preparation: normalizedPreparationConfig.name,
1006
- build_plan: buildPlanId ?? null,
1007
- source_folder_path: sourceFolderPath,
1008
- config_path: preparationConfigPath(asPreparationDataDir(prepDataDir)),
1009
- verifiable_context_path: preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), normalizedPreparationConfig.name),
1010
- changed: true,
1011
- message: operation === "select-build-plan"
1012
- ? `Preparation ${normalizedPreparationConfig.name} now uses Build Plan ${buildPlanId}.`
1013
- : buildPlanId
1014
- ? `Preparation ${normalizedPreparationConfig.name} is saved with Build Plan ${buildPlanId}.`
1015
- : `Preparation ${normalizedPreparationConfig.name} is saved. Draft or select a Build Plan before building.`,
1016
- });
1017
- }
1018
- applyPreparationChange(prepDataDir, requestValue) {
1019
- const request = PreparationChangeCreateRequestSchema.parse(requestValue);
1020
- if (request.confirmation !== request.preparation) {
1021
- throw new Error(`Type ${request.preparation} to confirm Preparation removal.`);
1022
- }
1023
- const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), request.preparation);
1024
- if (!preparation) {
1025
- throw new Error(`Preparation "${request.preparation}" is not saved.`);
1026
- }
1027
- removeSourcePreparationConfig(prepDataDir, request.preparation);
1028
- this.readinessCache.invalidatePreparation(prepDataDir, request.preparation);
1029
- this.buildRunCache.invalidatePreparation(prepDataDir, request.preparation);
1030
- this.verifyRunCache.invalidatePreparation(prepDataDir, request.preparation);
1031
- this.buildPlanListingCache.invalidate(prepDataDir);
1032
- return PreparationChangeResultSchema.parse({
1033
- kind: "interf-preparation-change-result",
1034
- version: 1,
1035
- operation: "remove",
1036
- preparation: request.preparation,
1037
- config_path: preparationConfigPath(asPreparationDataDir(prepDataDir)),
1038
- verifiable_context_path: preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), request.preparation),
1039
- verifiable_context_retained: true,
1040
- changed: true,
1041
- message: `Removed Preparation ${request.preparation}. Verifiable context files were retained.`,
1042
- });
1043
- }
1044
- applyReset(prepDataDir, requestValue) {
1045
- const request = ResetRequestSchema.parse(requestValue);
1046
- const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), request.preparation);
1047
- if (!preparation) {
1048
- throw new Error(`Preparation "${request.preparation}" is not saved.`);
1049
- }
1050
- const verifiableContextPath = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), request.preparation);
1051
- if (!existsSync(verifiableContextPath)) {
1052
- throw new Error(`Verifiable context for Preparation "${request.preparation}" does not exist.`);
1053
- }
1054
- resetBuildGeneratedState(verifiableContextPath, request.scope);
1055
- // Reset wipes generated state, including saved build/test/readiness records.
1056
- this.buildRunCache.invalidatePreparation(prepDataDir, request.preparation);
1057
- this.verifyRunCache.invalidatePreparation(prepDataDir, request.preparation);
1058
- this.readinessCache.invalidatePreparation(prepDataDir, request.preparation);
1059
- return ResetResultSchema.parse({
1060
- kind: "interf-reset-result",
1061
- version: 1,
1062
- preparation: request.preparation,
1063
- scope: request.scope,
1064
- verifiable_context_path: verifiableContextPath,
1065
- changed: true,
1066
- message: `Reset ${request.scope} state for Preparation ${request.preparation}.`,
1067
- });
1068
- }
1069
- async createBuildPlanAuthoringRun(prepDataDir, requestValue, jobType = "build-plan-draft") {
1070
- const parsedRequest = BuildPlanAuthoringCreateRequestSchema.parse(requestValue);
1071
- const savedPreparation = parsedRequest.preparation
1072
- ? findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), parsedRequest.preparation)
1073
- : null;
1074
- const request = {
1075
- ...parsedRequest,
1076
- requested_artifacts: parsedRequest.requested_artifacts.length > 0
1077
- ? parsedRequest.requested_artifacts
1078
- : savedPreparation?.requested_artifacts ?? [],
1079
- source_profile: parsedRequest.source_profile ?? savedPreparation?.source_profile ?? null,
1080
- };
1081
- const isImprovement = jobType === "build-plan-improvement";
1082
- const job = this.createJobRun(prepDataDir, {
1083
- job_type: jobType,
1084
- title: isImprovement ? `Improve Build Plan ${request.build_plan_id}` : `Draft Build Plan ${request.build_plan_id}`,
1085
- preparation: request.preparation ?? null,
1086
- build_plan: request.build_plan_id,
1087
- source_path: request.source_folder_path,
1088
- output_path: preparationBuildPlanPackagePath(asPreparationDataDir(prepDataDir), request.build_plan_id),
1089
- steps: [
1090
- {
1091
- id: "inspect-source",
1092
- label: "Inspect source files",
1093
- input: {
1094
- preparation: request.preparation ?? null,
1095
- source_folder_path: request.source_folder_path,
1096
- checks: request.checks.length,
1097
- requested_artifacts: request.requested_artifacts.length,
1098
- },
1099
- },
1100
- {
1101
- id: "draft-package",
1102
- label: isImprovement ? "Improve Build Plan definition" : "Draft Build Plan definition",
1103
- input: {
1104
- build_plan_id: request.build_plan_id,
1105
- label: request.label,
1106
- task_prompt: request.task_prompt,
1107
- requested_artifacts: request.requested_artifacts.length,
1108
- artifact_requirements: request.artifact_requirements.length,
1109
- },
1110
- },
1111
- {
1112
- id: "validate-package",
1113
- label: "Validate Build Plan definition",
1114
- input: {
1115
- build_plan_id: request.build_plan_id,
1116
- },
1117
- },
1118
- ],
1119
- });
1120
- this.appendJobRunEvent(prepDataDir, job.run_id, {
1121
- type: "step.started",
1122
- step_id: "inspect-source",
1123
- message: isImprovement ? "Inspecting source files for Build Plan improvement." : "Inspecting source files for Build Plan drafting.",
1124
- input: {
1125
- preparation: request.preparation ?? null,
1126
- source_folder_path: request.source_folder_path,
1127
- checks: request.checks.length,
1128
- requested_artifacts: request.requested_artifacts.length,
1129
- },
1130
- });
1131
- this.appendJobRunEvent(prepDataDir, job.run_id, {
1132
- type: "step.completed",
1133
- step_id: "inspect-source",
1134
- message: "Source context is ready.",
1135
- output: {
1136
- source_folder_path: request.source_folder_path,
1137
- checks: request.checks.length,
1138
- requested_artifacts: request.requested_artifacts.length,
1139
- },
1140
- });
1141
- this.appendJobRunEvent(prepDataDir, job.run_id, {
1142
- type: "step.started",
1143
- step_id: "draft-package",
1144
- message: isImprovement ? "Improving Build Plan definition." : "Drafting Build Plan definition.",
1145
- input: {
1146
- build_plan_id: request.build_plan_id,
1147
- label: request.label,
1148
- task_prompt: request.task_prompt,
1149
- requested_artifacts: request.requested_artifacts.length,
1150
- artifact_requirements: request.artifact_requirements.length,
1151
- },
1152
- });
1153
- void this.runBuildPlanAuthoringInBackground(prepDataDir, request, job.run_id);
1154
- return this.getJob(prepDataDir, job.run_id) ?? job;
1155
- }
1156
- listVerifiableContexts(prepDataDir) {
1157
- return listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))
1158
- .map((preparation) => this.getVerifiableContext(prepDataDir, preparation.name))
1159
- .filter((context) => context !== null);
1160
- }
1161
- getVerifiableContext(prepDataDir, preparationName) {
1162
- const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), preparationName);
1163
- if (!preparation)
1164
- return null;
1165
- const path = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
1166
- const config = readInterfConfig(path);
1167
- const buildRuns = this.listBuildRunsForPreparation(prepDataDir, preparation.name);
1168
- const verifyRuns = this.listVerifyRunsForPreparation(prepDataDir, preparation.name);
1169
- const readiness = this.computePreparationReadiness(prepDataDir, preparation);
1170
- const buildPlan = config?.build_plan ?? buildPlanIdForSourcePreparationConfig(preparation);
1171
- return VerifiableContextResourceSchema.parse({
1172
- preparation: preparation.name,
1173
- path,
1174
- exists: existsSync(path),
1175
- readiness,
1176
- build_plan: buildPlan,
1177
- latest_build_run_id: buildRuns[0]?.run_id ?? null,
1178
- latest_test_run_id: verifyRuns[0]?.run_id ?? null,
1179
- artifacts: uniqueArtifacts(buildRuns[0]?.stages.flatMap((stage) => stage.artifacts) ?? []),
1180
- });
1181
- }
1182
- listBuildRuns(prepDataDir) {
1183
- return byCreatedAtDesc(listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))
1184
- .flatMap((preparation) => this.listBuildRunsForPreparation(prepDataDir, preparation.name))).map((run) => BuildRunResourceSchema.parse({ run }));
1185
- }
1186
- listBuildRunsForPreparation(prepDataDir, preparationName) {
1187
- return this.buildRunCache.get(prepDataDir, preparationName, () => {
1188
- const verifiableContextPath = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), preparationName);
1189
- return byCreatedAtDesc(listJsonFiles(buildRunsRoot(verifiableContextPath))
1190
- .map(readBuildRunAt)
1191
- .filter((run) => run !== null)).map((run) => this.hydrateBuildRunFromRuntime(prepDataDir, verifiableContextPath, run));
1192
- }, (run) => run.run_id);
1193
- }
1194
- getBuildRun(prepDataDir, runId) {
1195
- // Fast path: if the runId was seen during a recent listing, look up
1196
- // its owning preparation directly and return that preparation's
1197
- // cached entry instead of scanning every preparation on disk.
1198
- const known = this.buildRunCache.preparationFor(prepDataDir, runId);
1199
- if (known) {
1200
- const found = this.listBuildRunsForPreparation(prepDataDir, known).find((entry) => entry.run_id === runId);
1201
- if (found)
1202
- return BuildRunResourceSchema.parse({ run: found });
1203
- }
1204
- // Slow path: scan all preparations. Falls through after a cache
1205
- // miss for an in-flight run created before this process restarted.
1206
- for (const resource of this.listBuildRuns(prepDataDir)) {
1207
- if (resource.run.run_id === runId)
1208
- return resource;
1209
- }
1210
- return null;
1211
- }
1212
- getBuildRunEvents(prepDataDir, runId) {
1213
- return this.getBuildRun(prepDataDir, runId)?.run.events ?? null;
1214
- }
1215
- getBuildRunProof(prepDataDir, runId) {
1216
- const run = this.getBuildRun(prepDataDir, runId)?.run;
1217
- if (!run)
1218
- return null;
1219
- return run.stages
1220
- .map((stage) => stage.latest_proof ?? null)
1221
- .filter((proof) => proof !== null);
1222
- }
1223
- getBuildRunArtifacts(prepDataDir, runId) {
1224
- const run = this.getBuildRun(prepDataDir, runId)?.run;
1225
- if (!run)
1226
- return null;
1227
- return uniqueArtifacts(run.stages.flatMap((stage) => stage.artifacts));
1228
- }
1229
- async createBuildRun(prepDataDir, requestValue) {
1230
- const request = BuildRunCreateRequestSchema.parse(requestValue);
1231
- const preparationConfig = this.resolvePreparationConfig(prepDataDir, request.preparation, {
1232
- build_plan: request.build_plan,
1233
- max_attempts: request.max_attempts,
1234
- max_loops: request.max_loops,
1235
- });
1236
- const verifiableContextPath = this.ensureVerifiableContextForRun(prepDataDir, preparationConfig);
1237
- const runId = createRunId("build");
1238
- const now = new Date().toISOString();
1239
- const buildPlan = getBuildPlan(requireSelectedBuildPlan(preparationConfig), {
1240
- prepDataDir,
1241
- });
1242
- const stageTotal = buildPlan.stages.length;
1243
- const run = BuildRunSchema.parse({
1244
- kind: "interf-build-run",
1245
- version: 1,
1246
- run_id: runId,
1247
- status: "running",
1248
- preparation: preparationConfig.name,
1249
- build_plan: buildPlan.id,
1250
- backend: "native",
1251
- source_path: resolveSourcePreparationPath(prepDataDir, preparationConfig),
1252
- verifiable_context_path: verifiableContextPath,
1253
- created_at: now,
1254
- started_at: now,
1255
- stages: buildPlan.stages
1256
- .map((stage, index) => {
1257
- return {
1258
- run_id: runId,
1259
- stage_id: stage.id,
1260
- stage_label: stage.label,
1261
- stage_index: index,
1262
- stage_total: stageTotal,
1263
- status: "queued",
1264
- contract: {
1265
- stage_label: stage.label,
1266
- stage_index: index,
1267
- stage_total: stageTotal,
1268
- reads: stage.reads,
1269
- writes: stage.writes,
1270
- },
1271
- artifacts: [],
1272
- };
1273
- }),
1274
- events: [],
1275
- });
1276
- this.writeBuildRun(prepDataDir, verifiableContextPath, run);
1277
- this.activeBuildRuns.set(runId, {
1278
- prepDataDir,
1279
- verifiableContextPath,
1280
- preparation: preparationConfig.name,
1281
- cancelled: false,
1282
- });
1283
- await this.recordBuildRunEvent(prepDataDir, verifiableContextPath, runId, {
1284
- type: "run.started",
1285
- event_id: createRunEventId("event"),
1286
- run_id: runId,
1287
- timestamp: now,
1288
- preparation: preparationConfig.name,
1289
- build_plan: buildPlan.id,
1290
- verifiable_context_path: verifiableContextPath,
1291
- backend: "native",
1292
- });
1293
- const sink = {
1294
- emit: (event) => this.recordBuildRunEvent(prepDataDir, verifiableContextPath, runId, event),
1295
- };
1296
- void this.runBuildInBackground(prepDataDir, request, {
1297
- runId,
1298
- sourcePath: prepDataDir,
1299
- verifiableContextPath,
1300
- preparationConfig,
1301
- events: sink,
1302
- });
1303
- const saved = this.readBuildRun(verifiableContextPath, runId) ?? run;
1304
- return BuildRunResourceSchema.parse({ run: saved });
1305
- }
1306
- /**
1307
- * Cancel an in-flight Build run. Marks the persisted record as
1308
- * `cancelled`, emits a `run.cancelled` event to capture the cancellation in
1309
- * the run timeline, and clears the active handle so retries may start a
1310
- * fresh run. If the run already finished, returns
1311
- * `{ cancelled: false, reason: "already finished" }` and persists nothing.
1312
- */
1313
- cancelBuildRun(runId) {
1314
- const handle = this.activeBuildRuns.get(runId);
1315
- if (!handle) {
1316
- // Either unknown or already terminal. The server route already 404s
1317
- // unknown ids before calling this, so anything reaching here is a run
1318
- // we already finalized.
1319
- return { cancelled: false, reason: "already finished" };
1320
- }
1321
- if (handle.cancelled) {
1322
- return { cancelled: false, reason: "already cancelled" };
1323
- }
1324
- const cancelledAt = new Date().toISOString();
1325
- handle.cancelled = true;
1326
- handle.cancelledAt = cancelledAt;
1327
- const current = this.readBuildRun(handle.verifiableContextPath, runId);
1328
- if (current && !isTerminalBuildRunStatus(current.status)) {
1329
- this.writeBuildRun(handle.prepDataDir, handle.verifiableContextPath, applyEventToBuildRun(current, {
1330
- type: "run.cancelled",
1331
- event_id: createRunEventId("event"),
1332
- run_id: runId,
1333
- timestamp: cancelledAt,
1334
- reason: "Build run cancelled by request.",
1335
- }));
1336
- }
1337
- return { cancelled: true };
1338
- }
1339
- /**
1340
- * Look up the run id previously associated with this idempotency key in
1341
- * `prepDataDir`. Returns null when the key is unknown or its TTL has
1342
- * elapsed. The preparation argument is required so that the same key in
1343
- * two different preparations always returns two different runs.
1344
- */
1345
- findIdempotentBuildRun(prepDataDir, key) {
1346
- const resolvedRoot = resolve(prepDataDir);
1347
- const bucket = this.idempotencyKeyCache.get(resolvedRoot);
1348
- if (!bucket)
1349
- return null;
1350
- const entry = bucket.get(key);
1351
- if (!entry)
1352
- return null;
1353
- if (entry.expiresAt <= Date.now()) {
1354
- // Opportunistic single-key prune. The bulk prune runs on writes
1355
- // when the cache crosses the size threshold (see
1356
- // {@link recordIdempotentBuildRun}).
1357
- bucket.delete(key);
1358
- if (bucket.size === 0)
1359
- this.idempotencyKeyCache.delete(resolvedRoot);
1360
- return null;
1361
- }
1362
- return entry.runId;
1363
- }
1364
- /**
1365
- * Cache the run id created (or returned) for this idempotency key in
1366
- * `prepDataDir`. Entries expire after `IDEMPOTENCY_TTL_MS`. Pruning
1367
- * is opportunistic: the previous implementation walked every entry on
1368
- * every read AND write, which was O(N) per request. Now we only sweep
1369
- * when the cache grows past {@link IDEMPOTENCY_PRUNE_THRESHOLD}.
1370
- */
1371
- recordIdempotentBuildRun(prepDataDir, key, runId) {
1372
- const resolvedRoot = resolve(prepDataDir);
1373
- let bucket = this.idempotencyKeyCache.get(resolvedRoot);
1374
- if (!bucket) {
1375
- bucket = new Map();
1376
- this.idempotencyKeyCache.set(resolvedRoot, bucket);
1377
- }
1378
- bucket.set(key, {
1379
- runId,
1380
- expiresAt: Date.now() + IDEMPOTENCY_TTL_MS,
1381
- });
1382
- if (this.totalIdempotencyEntries() > IDEMPOTENCY_PRUNE_THRESHOLD) {
1383
- this.pruneIdempotencyKeyCache();
1384
- }
1385
- }
1386
- /** Total cached idempotency entries across all preparations. */
1387
- totalIdempotencyEntries() {
1388
- let total = 0;
1389
- for (const bucket of this.idempotencyKeyCache.values())
1390
- total += bucket.size;
1391
- return total;
1392
- }
1393
- pruneIdempotencyKeyCache() {
1394
- const now = Date.now();
1395
- for (const [prepDataDir, bucket] of this.idempotencyKeyCache) {
1396
- for (const [key, entry] of bucket) {
1397
- if (entry.expiresAt <= now)
1398
- bucket.delete(key);
1399
- }
1400
- if (bucket.size === 0)
1401
- this.idempotencyKeyCache.delete(prepDataDir);
1402
- }
1403
- }
1404
- /**
1405
- * Test seam: force the cached entry for `key` in `prepDataDir` to be
1406
- * expired so the next lookup returns null. Returns true when an entry was
1407
- * found and expired. Tests use this in place of fake timers because the
1408
- * idempotency TTL is one hour and faking `Date.now()` would destabilize
1409
- * unrelated runtime state.
1410
- */
1411
- expireIdempotencyKeyForTesting(prepDataDir, key) {
1412
- const bucket = this.idempotencyKeyCache.get(resolve(prepDataDir));
1413
- if (!bucket)
1414
- return false;
1415
- const entry = bucket.get(key);
1416
- if (!entry)
1417
- return false;
1418
- entry.expiresAt = Date.now() - 1;
1419
- return true;
1420
- }
1421
- listVerifyRuns(prepDataDir) {
1422
- return newestFirst(listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))
1423
- .flatMap((preparation) => this.listVerifyRunsForPreparation(prepDataDir, preparation.name)));
1424
- }
1425
- listVerifyRunsForPreparation(prepDataDir, preparationName) {
1426
- return this.verifyRunCache.get(prepDataDir, preparationName, () => {
1427
- const verifiableContextPath = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), preparationName);
1428
- return newestFirst(listJsonFiles(verifyRunsRoot(verifiableContextPath))
1429
- .map(readVerifyRunAt)
1430
- .filter((run) => run !== null));
1431
- }, (run) => run.run_id);
1432
- }
1433
- getVerifyRun(prepDataDir, runId) {
1434
- const known = this.verifyRunCache.preparationFor(prepDataDir, runId);
1435
- if (known) {
1436
- const found = this.listVerifyRunsForPreparation(prepDataDir, known).find((run) => run.run_id === runId);
1437
- if (found)
1438
- return found;
1439
- }
1440
- return this.listVerifyRuns(prepDataDir).find((run) => run.run_id === runId) ?? null;
1441
- }
1442
- async createVerifyRun(prepDataDir, requestValue) {
1443
- const request = VerifyRunCreateRequestSchema.parse(requestValue);
1444
- const preparationConfig = this.resolvePreparationConfig(prepDataDir, request.preparation);
1445
- const verifiableContextPath = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), preparationConfig.name);
1446
- const verifiableContextTarget = createVerifiableContextTestTarget(verifiableContextPath, preparationConfig.name, buildPlanIdForSourcePreparationConfig(preparationConfig) ?? DEFAULT_BUILD_PLAN_ID);
1447
- const runId = createRunId("verify");
1448
- const now = new Date().toISOString();
1449
- const initial = VerifyRunResourceSchema.parse({
1450
- run_id: runId,
1451
- status: "running",
1452
- preparation: preparationConfig.name,
1453
- source_path: prepDataDir,
1454
- verifiable_context_path: verifiableContextTarget.eligible ? verifiableContextPath : null,
1455
- started_at: now,
1456
- readiness_run: null,
1457
- events: [],
1458
- });
1459
- this.writeVerifyRun(prepDataDir, verifiableContextPath, initial);
1460
- void this.runTestInBackground(prepDataDir, request, {
1461
- runId,
1462
- sourcePath: prepDataDir,
1463
- verifiableContextPath,
1464
- preparationConfig,
1465
- }, initial);
1466
- return initial;
1467
- }
1468
- async runBuildInBackground(prepDataDir, request, context) {
1469
- this.beginActiveRun();
1470
- try {
1471
- if (!this.handlers.createBuildRun) {
1472
- throw new Error("No build-run handler is configured for this local service.");
1473
- }
1474
- const result = LocalRunHandlerResultSchema.parse(await this.handlers.createBuildRun(request, context));
1475
- const wasCancelled = this.activeBuildRuns.get(context.runId)?.cancelled === true;
1476
- if (wasCancelled) {
1477
- // The run was cancelled while the handler was still running. The
1478
- // cancellation path already wrote a `cancelled` record; just refresh
1479
- // observability and skip emitting a second terminal event.
1480
- this.refreshBuildRunFromRuntime(prepDataDir, context.verifiableContextPath, context.runId);
1481
- await this.emitRuntimeDerivedEvents(prepDataDir, context.verifiableContextPath, context.runId);
1482
- await this.recordBuildRunEvent(prepDataDir, context.verifiableContextPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(prepDataDir, context.preparationConfig)));
1483
- return;
1484
- }
1485
- this.refreshBuildRunFromRuntime(prepDataDir, context.verifiableContextPath, context.runId);
1486
- await this.emitRuntimeDerivedEvents(prepDataDir, context.verifiableContextPath, context.runId);
1487
- if (!result.ok) {
1488
- await this.recordBuildRunEvent(prepDataDir, context.verifiableContextPath, context.runId, {
1489
- type: "run.failed",
1490
- event_id: createRunEventId("event"),
1491
- run_id: context.runId,
1492
- timestamp: createRunEventTimestamp(),
1493
- error: result.error ?? "Build run failed.",
1494
- });
1495
- }
1496
- else {
1497
- await this.recordBuildRunEvent(prepDataDir, context.verifiableContextPath, context.runId, {
1498
- type: "run.completed",
1499
- event_id: createRunEventId("event"),
1500
- run_id: context.runId,
1501
- timestamp: createRunEventTimestamp(),
1502
- summary: "Verifiable context ready.",
1503
- });
1504
- }
1505
- await this.recordBuildRunEvent(prepDataDir, context.verifiableContextPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(prepDataDir, context.preparationConfig)));
1506
- // 0.17 — emit per-Artifact billing events (stub form: JSONL).
1507
- // Production sink (Metronome HTTP) wires in 0.18+.
1508
- this.emitBillingEventsForRun(prepDataDir, context);
1509
- }
1510
- catch (error) {
1511
- await this.recordBuildRunEvent(prepDataDir, context.verifiableContextPath, context.runId, {
1512
- type: "run.failed",
1513
- event_id: createRunEventId("event"),
1514
- run_id: context.runId,
1515
- timestamp: createRunEventTimestamp(),
1516
- error: error instanceof Error ? error.message : String(error),
1517
- });
1518
- await this.recordBuildRunEvent(prepDataDir, context.verifiableContextPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(prepDataDir, context.preparationConfig)));
1519
- }
1520
- finally {
1521
- this.activeBuildRuns.delete(context.runId);
1522
- this.endActiveRun();
1523
- }
1524
- }
1525
- async runTestInBackground(prepDataDir, request, context, initial) {
1526
- this.beginActiveRun();
1527
- try {
1528
- if (!this.handlers.createVerifyRun) {
1529
- throw new Error("No verify-run handler is configured for this local service.");
1530
- }
1531
- const result = LocalRunHandlerResultSchema.parse(await this.handlers.createVerifyRun(request, context));
1532
- const readinessRun = result.readiness_run ?? this.readLatestReadinessRun(prepDataDir, context.preparationConfig.name);
1533
- const resultEvent = readinessRun
1534
- ? this.checksEvaluatedEvent(context.runId, readinessRun)
1535
- : null;
1536
- const nextWithoutReadiness = VerifyRunResourceSchema.parse({
1537
- ...initial,
1538
- status: result.ok ? "succeeded" : "failed",
1539
- finished_at: new Date().toISOString(),
1540
- readiness_run: readinessRun,
1541
- events: resultEvent ? [resultEvent] : [],
1542
- ...(!result.ok ? { error: result.error ?? "Readiness check failed." } : {}),
1543
- });
1544
- this.writeVerifyRun(prepDataDir, context.verifiableContextPath, nextWithoutReadiness);
1545
- const readiness = this.computePreparationReadiness(prepDataDir, context.preparationConfig);
1546
- const next = VerifyRunResourceSchema.parse({
1547
- ...nextWithoutReadiness,
1548
- readiness,
1549
- events: [
1550
- ...nextWithoutReadiness.events,
1551
- this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, readiness),
1552
- ],
1553
- });
1554
- this.writeVerifyRun(prepDataDir, context.verifiableContextPath, next);
1555
- }
1556
- catch (error) {
1557
- const failedWithoutReadiness = VerifyRunResourceSchema.parse({
1558
- ...initial,
1559
- status: "failed",
1560
- finished_at: new Date().toISOString(),
1561
- error: error instanceof Error ? error.message : String(error),
1562
- });
1563
- this.writeVerifyRun(prepDataDir, context.verifiableContextPath, failedWithoutReadiness);
1564
- const readiness = this.computePreparationReadiness(prepDataDir, context.preparationConfig);
1565
- const next = VerifyRunResourceSchema.parse({
1566
- ...failedWithoutReadiness,
1567
- readiness,
1568
- events: [this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, readiness)],
1569
- });
1570
- this.writeVerifyRun(prepDataDir, context.verifiableContextPath, next);
1571
- }
1572
- finally {
1573
- this.endActiveRun();
1574
- }
1575
- }
1576
- async runReadinessCheckDraftInBackground(prepDataDir, request, runId) {
1577
- this.beginActiveRun();
1578
- return this.runReadinessCheckDraftInBackgroundInner(prepDataDir, request, runId).finally(() => {
1579
- this.endActiveRun();
1580
- });
1581
- }
1582
- async runReadinessCheckDraftInBackgroundInner(prepDataDir, request, runId) {
1583
- try {
1584
- if (!this.handlers.createReadinessCheckDraft) {
1585
- throw new Error("No readiness-check-draft handler is configured for this local service.");
1586
- }
1587
- const result = ReadinessCheckDraftResultSchema.parse(await this.handlers.createReadinessCheckDraft(request, this.jobRunContext(prepDataDir, runId)));
1588
- this.appendJobRunEvent(prepDataDir, runId, {
1589
- type: "step.completed",
1590
- step_id: "agent-draft",
1591
- message: `Drafted ${result.checks.length} readiness checks.`,
1592
- output: {
1593
- checks: result.checks,
1594
- },
1595
- });
1596
- this.appendJobRunEvent(prepDataDir, runId, {
1597
- type: "step.started",
1598
- step_id: "normalize-checks",
1599
- message: "Normalizing drafted readiness checks into saved check records.",
1600
- input: {
1601
- checks: result.checks.length,
1602
- },
1603
- });
1604
- this.appendJobRunEvent(prepDataDir, runId, {
1605
- type: "step.completed",
1606
- step_id: "normalize-checks",
1607
- message: `${result.checks.length} readiness checks ready for review.`,
1608
- output: {
1609
- checks: result.checks.length,
1610
- },
1611
- });
1612
- this.setJobRunResult(prepDataDir, runId, result);
1613
- this.appendJobRunEvent(prepDataDir, runId, {
1614
- type: "job.completed",
1615
- message: `Drafted ${result.checks.length} readiness checks.`,
1616
- });
1617
- }
1618
- catch (error) {
1619
- const message = error instanceof Error ? error.message : String(error);
1620
- this.appendJobRunEvent(prepDataDir, runId, {
1621
- type: "step.failed",
1622
- step_id: "agent-draft",
1623
- message,
1624
- output: {
1625
- error: message,
1626
- },
1627
- });
1628
- this.appendJobRunEvent(prepDataDir, runId, {
1629
- type: "job.failed",
1630
- message,
1631
- });
1632
- }
1633
- }
1634
- async runBuildPlanAuthoringInBackground(prepDataDir, request, runId) {
1635
- this.beginActiveRun();
1636
- return this.runBuildPlanAuthoringInBackgroundInner(prepDataDir, request, runId).finally(() => {
1637
- this.endActiveRun();
1638
- });
1639
- }
1640
- async runBuildPlanAuthoringInBackgroundInner(prepDataDir, request, runId) {
1641
- try {
1642
- if (!this.handlers.createBuildPlanAuthoringRun) {
1643
- throw new Error("No Build Plan-authoring handler is configured for this local service.");
1644
- }
1645
- const result = BuildPlanAuthoringResultSchema.parse(await this.handlers.createBuildPlanAuthoringRun(request, this.jobRunContext(prepDataDir, runId)));
1646
- this.setJobRunResult(prepDataDir, runId, result);
1647
- this.appendJobRunEvent(prepDataDir, runId, {
1648
- type: result.status === "executor-failed" ? "step.failed" : "step.completed",
1649
- step_id: "draft-package",
1650
- message: result.summary,
1651
- output: {
1652
- status: result.status,
1653
- changed: result.changed,
1654
- build_plan_path: result.build_plan_path,
1655
- shell_path: result.shell_path,
1656
- },
1657
- });
1658
- this.appendJobRunEvent(prepDataDir, runId, {
1659
- type: "step.started",
1660
- step_id: "validate-package",
1661
- message: "Validating Build Plan package structure and stage contract.",
1662
- input: {
1663
- build_plan_path: result.build_plan_path,
1664
- },
1665
- });
1666
- if (result.status === "updated" || result.status === "no-change") {
1667
- let selectedPreparation = null;
1668
- if (request.preparation) {
1669
- const selected = this.resolvePreparationConfig(prepDataDir, request.preparation, {
1670
- build_plan: request.build_plan_id,
1671
- });
1672
- upsertSourcePreparationConfig(prepDataDir, selected);
1673
- this.readinessCache.invalidatePreparation(prepDataDir, selected.name);
1674
- this.buildRunCache.invalidatePreparation(prepDataDir, selected.name);
1675
- this.verifyRunCache.invalidatePreparation(prepDataDir, selected.name);
1676
- this.buildPlanListingCache.invalidate(prepDataDir);
1677
- selectedPreparation = selected.name;
1678
- }
1679
- this.appendJobRunEvent(prepDataDir, runId, {
1680
- type: "step.completed",
1681
- step_id: "validate-package",
1682
- message: selectedPreparation
1683
- ? `${result.summary} Selected Build Plan ${request.build_plan_id} for Preparation ${selectedPreparation}.`
1684
- : result.summary,
1685
- output: {
1686
- status: result.status,
1687
- validation: result.validation ?? null,
1688
- selected_preparation: selectedPreparation,
1689
- },
1690
- });
1691
- this.appendJobRunEvent(prepDataDir, runId, {
1692
- type: "job.completed",
1693
- message: selectedPreparation
1694
- ? `${result.summary} Selected Build Plan ${request.build_plan_id} for Preparation ${selectedPreparation}.`
1695
- : result.summary,
1696
- });
1697
- }
1698
- else {
1699
- this.appendJobRunEvent(prepDataDir, runId, {
1700
- type: "step.failed",
1701
- step_id: "validate-package",
1702
- message: result.summary,
1703
- output: {
1704
- status: result.status,
1705
- validation: result.validation ?? null,
1706
- },
1707
- });
1708
- this.appendJobRunEvent(prepDataDir, runId, {
1709
- type: "job.failed",
1710
- message: result.summary,
1711
- });
1712
- }
1713
- }
1714
- catch (error) {
1715
- const message = error instanceof Error ? error.message : String(error);
1716
- this.appendJobRunEvent(prepDataDir, runId, {
1717
- type: "step.failed",
1718
- step_id: "draft-package",
1719
- message,
1720
- output: {
1721
- error: message,
1722
- },
1723
- });
1724
- this.appendJobRunEvent(prepDataDir, runId, {
1725
- type: "job.failed",
1726
- message,
1727
- });
1728
- }
1729
- }
1730
- jobRunContext(prepDataDir, runId) {
1731
- return {
1732
- runId,
1733
- sourcePath: prepDataDir,
1734
- emit: (event) => {
1735
- this.appendJobRunEvent(prepDataDir, runId, event);
1736
- },
1737
- };
1738
- }
1739
- defaultPreparationName(prepDataDir) {
1740
- const preparation = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))[0];
1741
- if (!preparation) {
1742
- throw new Error("No Preparation is saved in this control plane folder.");
1743
- }
1744
- return preparation.name;
1745
- }
1746
- async planActionProposal(prepDataDir, request) {
1747
- if (!this.handlers.planActionProposal) {
1748
- return ActionProposalPlanSchema.parse({
1749
- action_type: "clarification",
1750
- ...(request.preparation ? { preparation: request.preparation } : {}),
1751
- assistant_message: "No local action planner is configured for this Interf instance.",
1752
- });
1753
- }
1754
- const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
1755
- let rawPlan;
1756
- try {
1757
- rawPlan = await this.handlers.planActionProposal(request, {
1758
- sourcePath: prepDataDir,
1759
- preparations,
1760
- preparationHealth: preparations.map((preparation) => {
1761
- const readinessChecks = preparation.checks?.length ?? 0;
1762
- const verifiableContextReady = hasVerifiableContextTestTarget(prepDataDir, preparation);
1763
- return {
1764
- name: preparation.name,
1765
- readiness_checks: readinessChecks,
1766
- verifiable_context_ready: verifiableContextReady,
1767
- can_check_readiness: readinessChecks > 0 && verifiableContextReady,
1768
- recommended_next_actions: verifiableContextReady
1769
- ? readinessChecks > 0
1770
- ? ["check readiness", "improve", "prepare again"]
1771
- : ["draft readiness checks", "prepare again"]
1772
- : ["prepare"],
1773
- };
1774
- }),
1775
- sourceFolders: listSourceFolderChoices(prepDataDir),
1776
- recentProposals: this.listActionProposals(prepDataDir).slice(0, 5),
1777
- });
1778
- }
1779
- catch {
1780
- return ActionProposalPlanSchema.parse({
1781
- action_type: "clarification",
1782
- ...(request.preparation ? { preparation: request.preparation } : {}),
1783
- assistant_message: ACTION_PLANNER_CLARIFICATION_MESSAGE,
1784
- });
1785
- }
1786
- const parsed = ActionProposalPlanSchema.safeParse(sanitizeActionProposalPlan(rawPlan));
1787
- if (parsed.success)
1788
- return parsed.data;
1789
- return ActionProposalPlanSchema.parse({
1790
- action_type: "clarification",
1791
- ...(request.preparation ? { preparation: request.preparation } : {}),
1792
- assistant_message: ACTION_PLANNER_CLARIFICATION_MESSAGE,
1793
- });
1794
- }
1795
- directServiceActionClarification(options) {
1796
- const endpoint = directServiceEndpointForAction(options.actionType);
1797
- if (!endpoint) {
1798
- throw new Error(`Action "${options.actionType}" is not a direct deterministic service action.`);
1799
- }
1800
- const label = options.actionType === "preparation-setup"
1801
- ? "Preparation setup"
1802
- : options.actionType === "build-plan-change"
1803
- ? "Build Plan change"
1804
- : "Preparation change";
1805
- const now = new Date().toISOString();
1806
- return ActionProposalResourceSchema.parse({
1807
- kind: "interf-action-proposal",
1808
- version: 1,
1809
- proposal_id: createActionProposalId(),
1810
- status: "needs_clarification",
1811
- action_type: "clarification",
1812
- title: `${label} uses a direct service endpoint`,
1813
- summary: `${label} is deterministic and is not accepted through action proposals.`,
1814
- assistant_message: `${label} must be submitted directly to ${endpoint}. Action proposals are only for freeform planning or async local-agent-backed work.`,
1815
- message: options.message,
1816
- preparation: options.preparation ?? null,
1817
- build_plan: options.buildPlan ?? null,
1818
- request: {
1819
- message: options.message,
1820
- endpoint,
1821
- action_type: options.actionType,
1822
- ...(options.values ? { values: options.values } : {}),
1823
- },
1824
- created_at: now,
1825
- updated_at: now,
1826
- proposed_by_executor: this.getExecutorStatus().executor,
1827
- approval: null,
1828
- submitted_run_id: null,
1829
- submitted_run_type: null,
1830
- error: null,
1831
- });
1832
- }
1833
- async buildActionProposal(prepDataDir, request) {
1834
- const structuredPreparationSetup = PreparationSetupActionValuesSchema.safeParse(request.values);
1835
- const structuredBuildPlanAuthoring = BuildPlanAuthoringActionValuesSchema.safeParse(request.values);
1836
- const structuredActionType = actionTypeFromValues(request.values);
1837
- const structuredDirectActionType = structuredPreparationSetup.success
1838
- ? "preparation-setup"
1839
- : structuredActionType && directServiceEndpointForAction(structuredActionType)
1840
- ? structuredActionType
1841
- : null;
1842
- if (structuredDirectActionType) {
1843
- return this.directServiceActionClarification({
1844
- actionType: structuredDirectActionType,
1845
- message: request.message,
1846
- buildPlan: stringValue(request.values, "build_plan") ??
1847
- stringValue(request.values, "build_plan_id") ??
1848
- stringValue(request.values, "new_build_plan_id"),
1849
- preparation: request.preparation ?? stringValue(request.values, "preparation") ?? stringValue(request.values, "name"),
1850
- values: request.values,
1851
- });
1852
- }
1853
- const structuredPlanActionType = structuredBuildPlanAuthoring.success ? "build-plan-draft" : structuredActionType;
1854
- // Typed UI/CLI actions already carry the service contract; only freeform chat needs planner inference.
1855
- const plan = structuredPlanActionType
1856
- ? ActionProposalPlanSchema.parse({
1857
- action_type: structuredPlanActionType,
1858
- ...(request.preparation ? { preparation: request.preparation } : {}),
1859
- })
1860
- : await this.planActionProposal(prepDataDir, request);
1861
- const actionType = structuredPlanActionType ?? plan.action_type;
1862
- if (directServiceEndpointForAction(actionType)) {
1863
- return this.directServiceActionClarification({
1864
- actionType,
1865
- message: request.message,
1866
- buildPlan: plan.build_plan ??
1867
- stringValue(plan.values, "build_plan") ??
1868
- stringValue(plan.values, "build_plan_id") ??
1869
- stringValue(request.values, "build_plan") ??
1870
- stringValue(request.values, "build_plan_id"),
1871
- preparation: plan.preparation ?? request.preparation ?? stringValue(plan.values, "preparation") ?? stringValue(request.values, "preparation"),
1872
- values: {
1873
- ...(plan.values ?? {}),
1874
- ...(request.values ?? {}),
1875
- },
1876
- });
1877
- }
1878
- const usePlannerText = !structuredPlanActionType && plan.action_type === actionType;
1879
- const now = new Date().toISOString();
1880
- if (actionType === "clarification") {
1881
- return ActionProposalResourceSchema.parse({
1882
- kind: "interf-action-proposal",
1883
- version: 1,
1884
- proposal_id: createActionProposalId(),
1885
- status: "needs_clarification",
1886
- action_type: actionType,
1887
- title: plan.title ?? "Clarify Interf action",
1888
- summary: plan.summary ?? "No local run was proposed because the request did not resolve to a confirmed Interf action.",
1889
- assistant_message: plan.assistant_message ?? ACTION_PLANNER_CLARIFICATION_MESSAGE,
1890
- ...(plan.command_preview ? { command_preview: plan.command_preview } : {}),
1891
- message: request.message,
1892
- preparation: plan.preparation ?? request.preparation ?? null,
1893
- build_plan: plan.build_plan ?? null,
1894
- request: {
1895
- message: request.message,
1896
- ...(plan.values ? { values: plan.values } : {}),
1897
- },
1898
- created_at: now,
1899
- updated_at: now,
1900
- proposed_by_executor: this.getExecutorStatus().executor,
1901
- approval: null,
1902
- submitted_run_id: null,
1903
- submitted_run_type: null,
1904
- error: null,
1905
- });
1906
- }
1907
- const proposalValues = {
1908
- ...(plan.values ?? {}),
1909
- ...(request.values ?? {}),
1910
- };
1911
- if (actionType === "build-plan-draft" || actionType === "build-plan-improvement") {
1912
- const requestedPreparationName = plan.preparation ?? request.preparation ?? null;
1913
- const fallbackPreparation = requestedPreparationName
1914
- ? null
1915
- : listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))[0] ?? null;
1916
- const preparationConfig = requestedPreparationName
1917
- ? this.resolvePreparationConfig(prepDataDir, requestedPreparationName)
1918
- : fallbackPreparation;
1919
- const preparationPath = preparationConfig
1920
- ? resolveSourcePreparationPath(prepDataDir, preparationConfig)
1921
- : resolveConfiguredSourceFolderPath(prepDataDir) ?? prepDataDir;
1922
- const requestedBuildPlanId = stringValue(request.values, "build_plan_id") ??
1923
- stringValue(request.values, "build_plan");
1924
- const plannedBuildPlanId = plan.build_plan ??
1925
- stringValue(plan.values, "build_plan_id") ??
1926
- stringValue(plan.values, "build_plan");
1927
- const buildPlanId = requestedBuildPlanId ?? plannedBuildPlanId ?? buildPlanIdForProposal(request.message, proposalValues);
1928
- const taskPrompt = actionValueBuildPlanTaskPrompt(proposalValues) ??
1929
- stringValue(proposalValues, "task_prompt") ??
1930
- buildPlanAuthoringPromptFallback(request.message, buildPlanId);
1931
- const hint = stringValue(proposalValues, "hint") ?? buildPlanAuthoringHintFromPrompt(taskPrompt);
1932
- const actionRequest = {
1933
- preparation: preparationConfig?.name ?? null,
1934
- source_folder_path: preparationPath,
1935
- build_plan_id: buildPlanId,
1936
- ...(stringValue(proposalValues, "base_build_plan_id") ?? stringValue(proposalValues, "base_build_plan_id")
1937
- ? { base_build_plan_id: stringValue(proposalValues, "base_build_plan_id") ?? stringValue(proposalValues, "base_build_plan_id") }
1938
- : {}),
1939
- ...(stringValue(proposalValues, "reference_build_plan_id") ?? stringValue(proposalValues, "reference_build_plan_id")
1940
- ? { reference_build_plan_id: stringValue(proposalValues, "reference_build_plan_id") ?? stringValue(proposalValues, "reference_build_plan_id") }
1941
- : {}),
1942
- label: stringValue(proposalValues, "label") ?? buildPlanLabelFromId(buildPlanId),
1943
- hint,
1944
- task_prompt: taskPrompt,
1945
- checks: preparationConfig?.checks ?? [],
1946
- requested_artifacts: preparationConfig?.requested_artifacts ?? [],
1947
- source_profile: preparationConfig?.source_profile ?? null,
1948
- artifact_requirements: artifactRequirementsFromRequestedArtifacts(preparationConfig?.requested_artifacts ?? []),
1949
- };
1950
- const commandPreview = (usePlannerText ? plan.command_preview : undefined) ??
1951
- actionCommandPreview(actionType, preparationConfig?.name ?? null, buildPlanId, proposalValues);
1952
- return ActionProposalResourceSchema.parse({
1953
- kind: "interf-action-proposal",
1954
- version: 1,
1955
- proposal_id: createActionProposalId(),
1956
- status: "awaiting_approval",
1957
- action_type: actionType,
1958
- title: (usePlannerText ? plan.title : undefined) ?? `Draft Build Plan ${buildPlanId}`,
1959
- summary: (usePlannerText ? plan.summary : undefined) ?? "Ask the configured local executor to create a reusable Build Plan.",
1960
- assistant_message: (usePlannerText ? plan.assistant_message : undefined) ??
1961
- actionAssistantMessage(actionType, preparationConfig?.name ?? null, commandPreview),
1962
- command_preview: commandPreview,
1963
- message: request.message,
1964
- preparation: preparationConfig?.name ?? null,
1965
- build_plan: buildPlanId,
1966
- request: actionRequest,
1967
- created_at: now,
1968
- updated_at: now,
1969
- proposed_by_executor: this.getExecutorStatus().executor,
1970
- approval: null,
1971
- submitted_run_id: null,
1972
- submitted_run_type: null,
1973
- error: null,
1974
- });
1975
- }
1976
- const preparationConfig = this.resolvePreparationConfig(prepDataDir, plan.preparation ?? request.preparation ?? this.defaultPreparationName(prepDataDir));
1977
- const proposalActionType = ActionProposalTypeSchema.parse(actionType);
1978
- const preparationPath = resolveSourcePreparationPath(prepDataDir, preparationConfig);
1979
- const requestedBuildPlanId = stringValue(request.values, "build_plan_id") ??
1980
- stringValue(request.values, "build_plan");
1981
- const plannedBuildPlanId = plan.build_plan ??
1982
- stringValue(plan.values, "build_plan_id") ??
1983
- stringValue(plan.values, "build_plan");
1984
- const buildPlanId = requestedBuildPlanId ?? plannedBuildPlanId ?? buildPlanIdForSourcePreparationConfig(preparationConfig);
1985
- const clarifyResolvedAction = (options) => ActionProposalResourceSchema.parse({
1986
- kind: "interf-action-proposal",
1987
- version: 1,
1988
- proposal_id: createActionProposalId(),
1989
- status: "needs_clarification",
1990
- action_type: "clarification",
1991
- title: options.title,
1992
- summary: options.summary,
1993
- assistant_message: options.assistantMessage,
1994
- message: request.message,
1995
- preparation: preparationConfig.name,
1996
- build_plan: buildPlanId ?? null,
1997
- request: {
1998
- message: request.message,
1999
- ...(proposalValues ? { values: proposalValues } : {}),
2000
- },
2001
- created_at: now,
2002
- updated_at: now,
2003
- proposed_by_executor: this.getExecutorStatus().executor,
2004
- approval: null,
2005
- submitted_run_id: null,
2006
- submitted_run_type: null,
2007
- error: null,
2008
- });
2009
- if (actionType === "test") {
2010
- const requestedMode = testModeFromValues(proposalValues);
2011
- const hasReadinessChecks = (preparationConfig.checks ?? []).length > 0;
2012
- const verifiableContextReady = hasVerifiableContextTestTarget(prepDataDir, preparationConfig);
2013
- if (!hasReadinessChecks) {
2014
- return clarifyResolvedAction({
2015
- title: `Add readiness checks for ${preparationConfig.name}`,
2016
- summary: "Readiness cannot be checked until this Preparation has saved checks.",
2017
- assistantMessage: `Preparation "${preparationConfig.name}" does not have saved readiness checks yet. Ask me to draft readiness checks after the Source Folder is prepared, or add readiness guidance first.`,
2018
- });
2019
- }
2020
- if (!verifiableContextReady && requestedMode !== "source-files") {
2021
- return clarifyResolvedAction({
2022
- title: `Build ${preparationConfig.name} first`,
2023
- summary: "Readiness checks need verifiable context unless you explicitly ask for a source-files-only baseline.",
2024
- assistantMessage: `Preparation "${preparationConfig.name}" has no built verifiable context yet. Build it first; after that Interf can check readiness. If you specifically want a source-files-only baseline, ask for source files only.`,
2025
- });
2026
- }
2027
- }
2028
- const actionRequest = (() => {
2029
- if (actionType === "build") {
2030
- return {
2031
- preparation: preparationConfig.name,
2032
- ...(buildPlanId ? { build_plan: buildPlanId } : {}),
2033
- };
2034
- }
2035
- if (actionType === "test") {
2036
- // 0.15 — verify runs always judge against the verifiable context
2037
- // verifiable context. The legacy `mode` field is gone from the
2038
- // wire request shape.
2039
- return {
2040
- preparation: preparationConfig.name,
2041
- };
2042
- }
2043
- if (actionType === "readiness-check-draft") {
2044
- return {
2045
- preparation: preparationConfig.name,
2046
- source_folder_path: preparationPath,
2047
- about: stringValue(proposalValues, "about") ?? preparationConfig.about ?? request.message,
2048
- target_count: Math.max(1, Math.min(8, Math.round(numberValue(proposalValues, "target_count") ?? 4))),
2049
- };
2050
- }
2051
- const fallbackBuildPlanId = buildPlanId ?? buildPlanIdForProposal(request.message, proposalValues);
2052
- const taskPrompt = actionValueBuildPlanTaskPrompt(proposalValues) ??
2053
- stringValue(proposalValues, "task_prompt") ??
2054
- buildPlanAuthoringPromptFallback(request.message, fallbackBuildPlanId);
2055
- const hint = stringValue(proposalValues, "hint") ?? buildPlanAuthoringHintFromPrompt(taskPrompt);
2056
- return {
2057
- preparation: preparationConfig.name,
2058
- source_folder_path: preparationPath,
2059
- build_plan_id: fallbackBuildPlanId,
2060
- ...(stringValue(proposalValues, "base_build_plan_id") ?? stringValue(proposalValues, "base_build_plan_id")
2061
- ? { base_build_plan_id: stringValue(proposalValues, "base_build_plan_id") ?? stringValue(proposalValues, "base_build_plan_id") }
2062
- : {}),
2063
- ...(stringValue(proposalValues, "reference_build_plan_id") ?? stringValue(proposalValues, "reference_build_plan_id")
2064
- ? { reference_build_plan_id: stringValue(proposalValues, "reference_build_plan_id") ?? stringValue(proposalValues, "reference_build_plan_id") }
2065
- : {}),
2066
- label: stringValue(proposalValues, "label") ?? buildPlanLabelFromId(fallbackBuildPlanId),
2067
- hint,
2068
- task_prompt: taskPrompt,
2069
- checks: preparationConfig.checks ?? [],
2070
- requested_artifacts: preparationConfig.requested_artifacts ?? [],
2071
- source_profile: preparationConfig.source_profile ?? null,
2072
- artifact_requirements: artifactRequirementsFromRequestedArtifacts(preparationConfig.requested_artifacts ?? []),
2073
- };
2074
- })();
2075
- const title = (() => {
2076
- if (plan.title)
2077
- return plan.title;
2078
- if (proposalActionType === "build")
2079
- return `Build ${preparationConfig.name}`;
2080
- if (proposalActionType === "test")
2081
- return `Check readiness for ${preparationConfig.name}`;
2082
- if (proposalActionType === "readiness-check-draft")
2083
- return `Draft readiness checks for ${preparationConfig.name}`;
2084
- return `Draft Build Plan ${buildPlanId}`;
2085
- })();
2086
- const summary = (() => {
2087
- if (plan.summary)
2088
- return plan.summary;
2089
- if (proposalActionType === "build")
2090
- return "Build verifiable context agents can use.";
2091
- if (proposalActionType === "test")
2092
- return "Run readiness checks against source files and verifiable context.";
2093
- if (proposalActionType === "readiness-check-draft")
2094
- return "Ask the configured local executor to draft saved readiness checks.";
2095
- return "Ask the configured local executor to create a reusable Build Plan.";
2096
- })();
2097
- const previewValues = proposalValues;
2098
- const commandPreview = plan.command_preview ?? actionCommandPreview(proposalActionType, preparationConfig.name, buildPlanId, previewValues);
2099
- return ActionProposalResourceSchema.parse({
2100
- kind: "interf-action-proposal",
2101
- version: 1,
2102
- proposal_id: createActionProposalId(),
2103
- status: "awaiting_approval",
2104
- action_type: proposalActionType,
2105
- title,
2106
- summary,
2107
- assistant_message: plan.assistant_message ?? actionAssistantMessage(proposalActionType, preparationConfig.name, commandPreview),
2108
- command_preview: commandPreview,
2109
- message: request.message,
2110
- preparation: preparationConfig.name,
2111
- build_plan: buildPlanId,
2112
- request: actionRequest,
2113
- created_at: now,
2114
- updated_at: now,
2115
- proposed_by_executor: this.getExecutorStatus().executor,
2116
- approval: null,
2117
- submitted_run_id: null,
2118
- submitted_run_type: null,
2119
- error: null,
2120
- });
2121
- }
2122
- async submitActionProposal(prepDataDir, proposal) {
2123
- if (proposal.action_type === "clarification") {
2124
- throw new Error("Clarification proposals cannot be submitted.");
2125
- }
2126
- if (proposal.action_type === "build") {
2127
- const resource = await this.createBuildRun(prepDataDir, proposal.request);
2128
- return {
2129
- runId: resource.run.run_id,
2130
- runType: "build-run",
2131
- };
2132
- }
2133
- if (proposal.action_type === "test") {
2134
- const resource = await this.createVerifyRun(prepDataDir, proposal.request);
2135
- return {
2136
- runId: resource.run_id,
2137
- runType: "verify-run",
2138
- };
2139
- }
2140
- if (proposal.action_type === "readiness-check-draft") {
2141
- const job = await this.createReadinessCheckDraftRun(prepDataDir, proposal.request);
2142
- return {
2143
- runId: job.run_id,
2144
- runType: "job-run",
2145
- };
2146
- }
2147
- const directEndpoint = directServiceEndpointForAction(proposal.action_type);
2148
- if (directEndpoint) {
2149
- throw new Error(`Action "${proposal.action_type}" must be submitted directly to ${directEndpoint}.`);
2150
- }
2151
- const job = await this.createBuildPlanAuthoringRun(prepDataDir, proposal.request, proposal.action_type === "build-plan-improvement" ? "build-plan-improvement" : "build-plan-draft");
2152
- return {
2153
- runId: job.run_id,
2154
- runType: "job-run",
2155
- };
2156
- }
2157
- resolvePreparationConfig(prepDataDir, preparationName, overrides = {}) {
2158
- const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), preparationName);
2159
- if (!preparation) {
2160
- throw new Error(`Preparation "${preparationName}" is not saved in this control plane folder.`);
2161
- }
2162
- const buildPlan = overrides.build_plan ?? buildPlanIdForSourcePreparationConfig(preparation) ?? undefined;
2163
- return {
2164
- ...preparation,
2165
- ...(buildPlan ? { build_plan: buildPlan } : {}),
2166
- ...(typeof overrides.max_attempts === "number" ? { max_attempts: overrides.max_attempts } : {}),
2167
- ...(typeof overrides.max_loops === "number" ? { max_loops: overrides.max_loops } : {}),
2168
- };
2169
- }
2170
- ensureVerifiableContextForRun(prepDataDir, preparationConfig) {
2171
- const buildPlanId = requireSelectedBuildPlan(preparationConfig);
2172
- const verifiableContextPath = ensureVerifiableContextScaffold(prepDataDir, preparationConfig.name, buildPlanId);
2173
- syncVerifiableContextInterfConfigFromSourcePreparationConfig(verifiableContextPath, preparationConfig);
2174
- return verifiableContextPath;
2175
- }
2176
- readBuildRun(verifiableContextPath, runId) {
2177
- return readBuildRunAt(buildRunPath(verifiableContextPath, runId));
2178
- }
2179
- hydrateBuildRunFromRuntime(prepDataDir, verifiableContextPath, run) {
2180
- if (!isTerminalBuildRunStatus(run.status))
2181
- return run;
2182
- this.refreshBuildRunFromRuntime(prepDataDir, verifiableContextPath, run.run_id);
2183
- return this.readBuildRun(verifiableContextPath, run.run_id) ?? run;
2184
- }
2185
- finalizeInterruptedBuildRuns(prepDataDir) {
2186
- let preparations;
2187
- try {
2188
- preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
2189
- }
2190
- catch {
2191
- return;
2192
- }
2193
- for (const preparation of preparations) {
2194
- const verifiableContextPath = preparationVerifiableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
2195
- for (const run of listJsonFiles(buildRunsRoot(verifiableContextPath))
2196
- .map(readBuildRunAt)
2197
- .filter((entry) => entry !== null)) {
2198
- if (isTerminalBuildRunStatus(run.status) || this.activeBuildRuns.has(run.run_id))
2199
- continue;
2200
- const timestamp = createRunEventTimestamp();
2201
- const interruptedRun = {
2202
- ...run,
2203
- stages: run.stages.map((stage) => {
2204
- if (stage.status !== "running")
2205
- return stage;
2206
- return {
2207
- ...stage,
2208
- status: "failed",
2209
- finished_at: stage.finished_at ?? timestamp,
2210
- summary: stage.summary ?? INTERRUPTED_COMPILE_RUN_MESSAGE,
2211
- failure: stage.failure ?? INTERRUPTED_COMPILE_RUN_MESSAGE,
2212
- };
2213
- }),
2214
- };
2215
- this.writeBuildRun(prepDataDir, verifiableContextPath, applyEventToBuildRun(interruptedRun, {
2216
- type: "run.cancelled",
2217
- event_id: createRunEventId("event"),
2218
- run_id: run.run_id,
2219
- timestamp,
2220
- reason: INTERRUPTED_COMPILE_RUN_MESSAGE,
2221
- }));
2222
- }
2223
- }
2224
- }
2225
- finalizeInterruptedJobRuns(prepDataDir) {
2226
- for (const run of this.listJobs(prepDataDir)) {
2227
- if (isTerminalJobStatus(run.status))
2228
- continue;
2229
- const timestamp = createRunEventTimestamp();
2230
- const interruptedRun = LocalJobRunResourceSchema.parse({
2231
- ...run,
2232
- steps: run.steps.map((step) => {
2233
- if (step.status !== "running")
2234
- return step;
2235
- return {
2236
- ...step,
2237
- status: "failed",
2238
- finished_at: step.finished_at ?? timestamp,
2239
- summary: step.summary ?? INTERRUPTED_JOB_RUN_MESSAGE,
2240
- };
2241
- }),
2242
- });
2243
- this.writeJobRun(prepDataDir, applyEventToLocalJob(interruptedRun, {
2244
- type: "job.failed",
2245
- event_id: createRunEventId("event"),
2246
- run_id: run.run_id,
2247
- timestamp,
2248
- message: INTERRUPTED_JOB_RUN_MESSAGE,
2249
- }));
2250
- }
2251
- }
2252
- /**
2253
- * 0.17 — emit per-Artifact billing events when a Build run reaches
2254
- * a terminal state. STUB FORM: writes a JSONL file alongside the run
2255
- * record by default. Production sink (Metronome HTTP) wires in 0.18+.
2256
- * The JSONL output is observability/dev fixture, NOT production
2257
- * billing data.
2258
- */
2259
- emitBillingEventsForRun(prepDataDir, context) {
2260
- try {
2261
- const run = this.readBuildRun(context.verifiableContextPath, context.runId);
2262
- if (!run || run.artifacts.length === 0)
2263
- return;
2264
- const sink = this.billingEventSink ?? new JsonlBillingEventSink(defaultBillingEventLogPath({
2265
- preparationDataDir: prepDataDir,
2266
- preparationName: run.preparation,
2267
- runId: context.runId,
2268
- }));
2269
- const events = buildCompilationEventsForRun({
2270
- runId: context.runId,
2271
- preparation: run.preparation,
2272
- buildPlanId: run.build_plan,
2273
- accountId: null, // 0.17 — loopback only; cloud variant fills via tokenValidator (B4.3).
2274
- artifacts: run.artifacts,
2275
- startedAt: run.started_at ?? null,
2276
- finishedAt: run.finished_at ?? null,
2277
- });
2278
- for (const event of events) {
2279
- sink.emit(event);
2280
- }
2281
- }
2282
- catch {
2283
- // Billing is observability-only in 0.17; never let stub failures
2284
- // block a successful build.
2285
- }
2286
- }
2287
- writeBuildRun(prepDataDir, verifiableContextPath, run) {
2288
- mkdirSync(buildRunsRoot(verifiableContextPath), { recursive: true });
2289
- writeJsonFile(buildRunPath(verifiableContextPath, run.run_id), BuildRunSchema.parse(run));
2290
- // Bust per-preparation list + readiness caches so the next read
2291
- // reflects the write. We invalidate broadly (per-preparation, not
2292
- // per-record) because list recompute cost is bounded by run count
2293
- // and the simpler model avoids fan-out bugs.
2294
- this.buildRunCache.invalidatePreparation(prepDataDir, run.preparation);
2295
- this.readinessCache.invalidatePreparation(prepDataDir, run.preparation);
2296
- }
2297
- writeJobRun(prepDataDir, run) {
2298
- mkdirSync(localJobsRoot(prepDataDir), { recursive: true });
2299
- writeJsonFile(localJobPath(prepDataDir, run.run_id), LocalJobRunResourceSchema.parse(run));
2300
- if (run.preparation) {
2301
- // Some job runs (readiness-check drafts) flip readiness state.
2302
- this.readinessCache.invalidatePreparation(prepDataDir, run.preparation);
2303
- }
2304
- }
2305
- writeActionProposal(prepDataDir, proposal) {
2306
- mkdirSync(actionProposalsRoot(prepDataDir), { recursive: true });
2307
- writeJsonFile(actionProposalPath(prepDataDir, proposal.proposal_id), ActionProposalResourceSchema.parse(proposal));
2308
- }
2309
- setJobRunResult(prepDataDir, runId, result) {
2310
- const current = this.getJob(prepDataDir, runId);
2311
- if (!current)
2312
- return;
2313
- const normalizedResult = result && typeof result === "object" && !Array.isArray(result)
2314
- ? result
2315
- : { value: result };
2316
- this.writeJobRun(prepDataDir, LocalJobRunResourceSchema.parse({
2317
- ...current,
2318
- result: normalizedResult,
2319
- }));
2320
- }
2321
- async recordBuildRunEvent(prepDataDir, verifiableContextPath, runId, event) {
2322
- const current = this.readBuildRun(verifiableContextPath, runId);
2323
- if (!current)
2324
- return;
2325
- this.writeBuildRun(prepDataDir, verifiableContextPath, applyEventToBuildRun(current, event));
2326
- if (event.type === "stage.passed" || event.type === "stage.failed") {
2327
- this.refreshBuildRunFromRuntime(prepDataDir, verifiableContextPath, runId);
2328
- }
2329
- }
2330
- refreshBuildRunFromRuntime(prepDataDir, verifiableContextPath, runId) {
2331
- const current = this.readBuildRun(verifiableContextPath, runId);
2332
- if (!current)
2333
- return;
2334
- const state = loadState(verifiableContextPath);
2335
- if (!state?.stages)
2336
- return;
2337
- const historyByStage = new Map();
2338
- for (const run of readRuntimeRunHistory(verifiableContextPath)) {
2339
- if (run.target_name !== current.preparation)
2340
- continue;
2341
- const existing = historyByStage.get(run.stage);
2342
- if (!existing || timestampKey(run.updated_at) >= timestampKey(existing.updated_at)) {
2343
- historyByStage.set(run.stage, run);
2344
- }
2345
- }
2346
- const next = {
2347
- ...current,
2348
- stages: current.stages.map((stage) => {
2349
- const stageState = state.stages?.[stage.stage_id];
2350
- if (!stageState)
2351
- return stage;
2352
- const runtimeRun = historyByStage.get(stage.stage_id);
2353
- const artifacts = uniqueArtifacts([
2354
- ...(stage.artifacts ?? []),
2355
- ...stageArtifactRefs(stage.stage_id, stageState.artifacts),
2356
- ]);
2357
- const proof = stage.latest_proof ?? proofForStage({
2358
- runId,
2359
- stageId: stage.stage_id,
2360
- summary: stageState.summary,
2361
- stageState,
2362
- artifacts,
2363
- });
2364
- return {
2365
- ...stage,
2366
- started_at: stage.started_at ?? stageState.started_at,
2367
- finished_at: stage.finished_at ?? stageState.finished_at,
2368
- summary: stage.summary ?? stageState.summary ?? null,
2369
- executor: stage.executor ?? runtimeRun?.executor ?? null,
2370
- artifacts,
2371
- logs: stage.logs ?? logsForRuntimeRun(runtimeRun) ?? logsForStageRun(stageState),
2372
- latest_proof: proof,
2373
- };
2374
- }),
2375
- };
2376
- next.latest_proof = [...next.stages].reverse().find((stage) => Boolean(stage.latest_proof))?.latest_proof ?? next.latest_proof;
2377
- // Recompute per-Artifact status whenever the Build run is
2378
- // refreshed from runtime state.
2379
- try {
2380
- const buildPlan = getBuildPlan(current.build_plan, { prepDataDir });
2381
- next.artifacts = computeArtifactStatuses({
2382
- buildPlan,
2383
- verifiableContextPath,
2384
- stageRuns: next.stages,
2385
- counts: countsFromVerifiableContextState(state),
2386
- });
2387
- }
2388
- catch {
2389
- next.artifacts = current.artifacts ?? [];
2390
- }
2391
- this.writeBuildRun(prepDataDir, verifiableContextPath, next);
2392
- }
2393
- async emitRuntimeDerivedEvents(prepDataDir, verifiableContextPath, runId) {
2394
- const state = loadState(verifiableContextPath);
2395
- const run = this.readBuildRun(verifiableContextPath, runId);
2396
- if (!state?.stages || !run)
2397
- return;
2398
- for (const stage of run.stages) {
2399
- const stageState = state.stages[stage.stage_id];
2400
- if (!stageState)
2401
- continue;
2402
- const artifacts = stageArtifactRefs(stage.stage_id, stageState.artifacts);
2403
- for (const artifact of artifacts) {
2404
- await this.recordBuildRunEvent(prepDataDir, verifiableContextPath, runId, {
2405
- type: "artifact.written",
2406
- event_id: createRunEventId("event"),
2407
- run_id: runId,
2408
- timestamp: stageState.finished_at ?? createRunEventTimestamp(),
2409
- stage_id: stage.stage_id,
2410
- artifact,
2411
- });
2412
- }
2413
- await this.recordBuildRunEvent(prepDataDir, verifiableContextPath, runId, {
2414
- type: "proof.updated",
2415
- event_id: createRunEventId("event"),
2416
- run_id: runId,
2417
- timestamp: stageState.finished_at ?? createRunEventTimestamp(),
2418
- stage_id: stage.stage_id,
2419
- proof: proofForStage({
2420
- runId,
2421
- stageId: stage.stage_id,
2422
- summary: stageState.summary,
2423
- stageState,
2424
- artifacts,
2425
- }),
2426
- });
2427
- }
2428
- }
2429
- readLatestReadinessRun(prepDataDir, preparationName) {
2430
- return readSavedReadinessCheckRun(prepDataDir, preparationName);
2431
- }
2432
- checksEvaluatedEvent(runId, readinessRun) {
2433
- const target = readinessRun.verifiable_context ?? readinessRun.source_files;
2434
- return {
2435
- type: "checks.evaluated",
2436
- event_id: createRunEventId("event"),
2437
- run_id: runId,
2438
- timestamp: createRunEventTimestamp(),
2439
- passed: target?.passed_cases ?? 0,
2440
- total: target?.total_cases ?? 0,
2441
- test_run_id: runId,
2442
- };
2443
- }
2444
- readinessUpdatedEvent(runId, preparation, readiness) {
2445
- return {
2446
- type: "readiness.updated",
2447
- event_id: createRunEventId("event"),
2448
- run_id: runId,
2449
- timestamp: createRunEventTimestamp(),
2450
- preparation,
2451
- readiness,
2452
- };
2453
- }
2454
- writeVerifyRun(prepDataDir, verifiableContextPath, run) {
2455
- mkdirSync(verifyRunsRoot(verifiableContextPath), { recursive: true });
2456
- writeJsonFile(verifyRunPath(verifiableContextPath, run.run_id), VerifyRunResourceSchema.parse(run));
2457
- this.verifyRunCache.invalidatePreparation(prepDataDir, run.preparation);
2458
- this.readinessCache.invalidatePreparation(prepDataDir, run.preparation);
2459
- }
2460
- }
2461
- export function createLocalServiceRuntime(options) {
2462
- return new LocalServiceRuntime(options);
2463
- }