@interf/compiler 0.7.3 → 0.9.1

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 (302) hide show
  1. package/README.md +123 -89
  2. package/TRADEMARKS.md +1 -1
  3. package/agent-skills/interf-actions/SKILL.md +71 -0
  4. package/agent-skills/interf-actions/references/cli.md +190 -0
  5. package/apps/compiler-ui/.next/server/app/index.html +1 -0
  6. package/apps/compiler-ui/.next/static/XWKL548yXD_UOG4ID9G3J/_buildManifest.js +11 -0
  7. package/apps/compiler-ui/.next/static/XWKL548yXD_UOG4ID9G3J/_clientMiddlewareManifest.js +1 -0
  8. package/apps/compiler-ui/.next/static/XWKL548yXD_UOG4ID9G3J/_ssgManifest.js +1 -0
  9. package/apps/compiler-ui/.next/static/chunks/03~yq9q893hmn.js +1 -0
  10. package/apps/compiler-ui/.next/static/chunks/06z~l3kwb891e.js +1 -0
  11. package/apps/compiler-ui/.next/static/chunks/08g7lvje.te.u.js +1 -0
  12. package/apps/compiler-ui/.next/static/chunks/08m7vf5asqlsm.js +91 -0
  13. package/apps/compiler-ui/.next/static/chunks/0_i-3_5l9t2qe.js +1 -0
  14. package/apps/compiler-ui/.next/static/chunks/0b-ywny_j0g~0.js +1 -0
  15. package/apps/compiler-ui/.next/static/chunks/0b52v41o1gixx.js +1 -0
  16. package/apps/compiler-ui/.next/static/chunks/0fgt_8knmicoz.js +124 -0
  17. package/apps/compiler-ui/.next/static/chunks/0gpzgsv0w.q~m.js +31 -0
  18. package/apps/compiler-ui/.next/static/chunks/0ilwfezfvu6~-.js +1 -0
  19. package/apps/compiler-ui/.next/static/chunks/0n51hrfoufc7g.js +1 -0
  20. package/apps/compiler-ui/.next/static/chunks/0ti_66mx7~w2-.js +5 -0
  21. package/apps/compiler-ui/.next/static/chunks/0xxmf45eskdt~.css +1 -0
  22. package/apps/compiler-ui/.next/static/chunks/0y5z3t-z1c8ks.js.map +5 -0
  23. package/apps/compiler-ui/.next/static/chunks/13g~4mamjft.c.css +3 -0
  24. package/apps/compiler-ui/.next/static/chunks/14wtz~vq25~qq.js +1 -0
  25. package/apps/compiler-ui/.next/static/chunks/turbopack-109rtik40vwh5.js +1 -0
  26. package/apps/compiler-ui/.next/static/chunks/turbopack-10e~t1yzi4svj.js +1 -0
  27. package/apps/compiler-ui/.next/static/chunks/turbopack-worker-0sjn--fhq~1cg.js +1 -0
  28. package/apps/compiler-ui/.next/static/media/GeistMono_Variable.p.17jn9btb_52pq.woff2 +0 -0
  29. package/apps/compiler-ui/.next/static/media/Geist_Variable-s.p.0-te~ja_gpvcf.woff2 +0 -0
  30. package/apps/compiler-ui/.next/static/media/worker.102zas1s52_pf.js +109 -0
  31. package/builtin-workflows/interf/README.md +8 -8
  32. package/builtin-workflows/interf/compile/stages/shape/SKILL.md +4 -4
  33. package/builtin-workflows/interf/compile/stages/structure/SKILL.md +1 -1
  34. package/builtin-workflows/interf/improve/SKILL.md +1 -1
  35. package/builtin-workflows/interf/use/query/SKILL.md +1 -1
  36. package/builtin-workflows/interf/workflow.json +8 -8
  37. package/builtin-workflows/interf/workflow.schema.json +1 -1
  38. package/dist/cli/commands/check-draft.d.ts +5 -4
  39. package/dist/cli/commands/check-draft.js +18 -17
  40. package/dist/cli/commands/compile-controller.d.ts +3 -0
  41. package/dist/cli/commands/compile-controller.js +53 -45
  42. package/dist/cli/commands/compile.js +83 -27
  43. package/dist/cli/commands/compiled-flow.d.ts +3 -0
  44. package/dist/cli/commands/compiled-flow.js +9 -7
  45. package/dist/cli/commands/control-path.d.ts +11 -0
  46. package/dist/cli/commands/control-path.js +72 -0
  47. package/dist/cli/commands/create-workflow-wizard.d.ts +6 -0
  48. package/dist/cli/commands/create-workflow-wizard.js +191 -77
  49. package/dist/cli/commands/create.js +54 -40
  50. package/dist/cli/commands/default.js +4 -3
  51. package/dist/cli/commands/doctor.js +5 -5
  52. package/dist/cli/commands/executor-flow.js +1 -1
  53. package/dist/cli/commands/init.js +326 -76
  54. package/dist/cli/commands/list.js +8 -7
  55. package/dist/cli/commands/preparation-selection.d.ts +6 -0
  56. package/dist/cli/commands/preparation-selection.js +11 -0
  57. package/dist/cli/commands/reset.js +3 -3
  58. package/dist/cli/commands/source-config-wizard.d.ts +4 -3
  59. package/dist/cli/commands/source-config-wizard.js +175 -197
  60. package/dist/cli/commands/status.js +116 -8
  61. package/dist/cli/commands/test-flow.d.ts +1 -1
  62. package/dist/cli/commands/test-flow.js +31 -31
  63. package/dist/cli/commands/test.d.ts +4 -0
  64. package/dist/cli/commands/test.js +95 -58
  65. package/dist/cli/commands/verify.js +7 -7
  66. package/dist/cli/commands/web.d.ts +11 -0
  67. package/dist/cli/commands/web.js +373 -0
  68. package/dist/cli/index.d.ts +2 -1
  69. package/dist/cli/index.js +3 -1
  70. package/dist/index.d.ts +4 -2
  71. package/dist/index.js +3 -1
  72. package/dist/packages/agents/lib/execution.js +4 -2
  73. package/dist/packages/agents/lib/executors.d.ts +1 -0
  74. package/dist/packages/agents/lib/executors.js +1 -0
  75. package/dist/packages/agents/lib/render.d.ts +2 -2
  76. package/dist/packages/agents/lib/render.js +10 -9
  77. package/dist/packages/agents/lib/shells.d.ts +3 -11
  78. package/dist/packages/agents/lib/shells.js +45 -96
  79. package/dist/packages/agents/lib/types.d.ts +1 -0
  80. package/dist/packages/compiler/compiled-paths.d.ts +4 -1
  81. package/dist/packages/compiler/compiled-paths.js +7 -1
  82. package/dist/packages/compiler/compiled-pipeline.d.ts +7 -0
  83. package/dist/packages/compiler/compiled-pipeline.js +15 -4
  84. package/dist/packages/compiler/compiled-schema.js +1 -1
  85. package/dist/packages/compiler/compiled-stage-plan.d.ts +2 -2
  86. package/dist/packages/compiler/compiled-stage-plan.js +4 -4
  87. package/dist/packages/compiler/compiled-stage-runner.d.ts +5 -0
  88. package/dist/packages/compiler/compiled-stage-runner.js +50 -2
  89. package/dist/packages/compiler/compiled-target.d.ts +1 -1
  90. package/dist/packages/compiler/compiled-target.js +2 -2
  91. package/dist/packages/compiler/lib/schema.d.ts +161 -1
  92. package/dist/packages/compiler/lib/schema.js +87 -1
  93. package/dist/packages/compiler/raw-snapshot.d.ts +9 -2
  94. package/dist/packages/compiler/runtime-acceptance.js +1 -1
  95. package/dist/packages/compiler/runtime-contracts.js +2 -2
  96. package/dist/packages/compiler/runtime-prompt.js +6 -6
  97. package/dist/packages/compiler/runtime-reconcile.js +3 -3
  98. package/dist/packages/compiler/runtime-runs.js +3 -0
  99. package/dist/packages/compiler/runtime-types.d.ts +1 -0
  100. package/dist/packages/compiler/state-health.js +1 -1
  101. package/dist/packages/compiler/state-view.js +2 -2
  102. package/dist/packages/compiler/validate-compiled.js +11 -9
  103. package/dist/packages/execution/adapters.d.ts +15 -0
  104. package/dist/packages/execution/adapters.js +1 -0
  105. package/dist/packages/execution/events.d.ts +8 -0
  106. package/dist/packages/execution/events.js +16 -0
  107. package/dist/packages/execution/index.d.ts +9 -0
  108. package/dist/packages/execution/index.js +6 -0
  109. package/dist/packages/execution/lib/schema.d.ts +1554 -0
  110. package/dist/packages/execution/lib/schema.js +319 -0
  111. package/dist/packages/local-service/action-values.d.ts +23 -0
  112. package/dist/packages/local-service/action-values.js +31 -0
  113. package/dist/packages/local-service/client.d.ts +119 -0
  114. package/dist/packages/local-service/client.js +356 -0
  115. package/dist/packages/local-service/index.d.ts +15 -0
  116. package/dist/packages/local-service/index.js +11 -0
  117. package/dist/packages/local-service/lib/schema.d.ts +7355 -0
  118. package/dist/packages/local-service/lib/schema.js +636 -0
  119. package/dist/packages/local-service/routes.d.ts +27 -0
  120. package/dist/packages/local-service/routes.js +32 -0
  121. package/dist/packages/local-service/runtime.d.ts +134 -0
  122. package/dist/packages/local-service/runtime.js +2841 -0
  123. package/dist/packages/local-service/server.d.ts +17 -0
  124. package/dist/packages/local-service/server.js +572 -0
  125. package/dist/packages/project-model/index.d.ts +2 -1
  126. package/dist/packages/project-model/index.js +1 -0
  127. package/dist/packages/project-model/interf-detect.d.ts +6 -2
  128. package/dist/packages/project-model/interf-detect.js +25 -10
  129. package/dist/packages/project-model/interf-scaffold.js +9 -6
  130. package/dist/packages/project-model/interf.d.ts +1 -1
  131. package/dist/packages/project-model/interf.js +1 -1
  132. package/dist/packages/project-model/lib/schema.d.ts +80 -6
  133. package/dist/packages/project-model/lib/schema.js +29 -19
  134. package/dist/packages/project-model/source-config.d.ts +22 -13
  135. package/dist/packages/project-model/source-config.js +140 -123
  136. package/dist/packages/project-model/source-folders.d.ts +11 -0
  137. package/dist/packages/project-model/source-folders.js +110 -0
  138. package/dist/packages/testing/test-execution.js +5 -5
  139. package/dist/packages/testing/test-paths.js +1 -3
  140. package/dist/packages/workflow-authoring/workflow-authoring.d.ts +4 -6
  141. package/dist/packages/workflow-authoring/workflow-authoring.js +45 -86
  142. package/dist/packages/workflow-authoring/workflow-edit-session.d.ts +2 -0
  143. package/dist/packages/workflow-authoring/workflow-edit-session.js +80 -49
  144. package/dist/packages/workflow-authoring/workflow-improvement.js +2 -1
  145. package/dist/packages/workflow-package/context-interface.js +1 -1
  146. package/dist/packages/workflow-package/interf-workflow-package.d.ts +6 -0
  147. package/dist/packages/workflow-package/interf-workflow-package.js +115 -8
  148. package/dist/packages/workflow-package/local-workflows.js +33 -17
  149. package/dist/packages/workflow-package/workflow-definitions.d.ts +5 -5
  150. package/dist/packages/workflow-package/workflow-definitions.js +23 -22
  151. package/dist/packages/workflow-package/workflow-helpers.js +10 -6
  152. package/dist/packages/workflow-package/workflow-stage-runner.d.ts +1 -0
  153. package/dist/packages/workflow-package/workflow-stage-runner.js +2 -0
  154. package/package.json +17 -1
  155. package/dist/cli/commands/dataset-selection.d.ts +0 -6
  156. package/dist/cli/commands/dataset-selection.js +0 -11
  157. package/dist/lib/agent-args.d.ts +0 -1
  158. package/dist/lib/agent-args.js +0 -1
  159. package/dist/lib/agent-constants.d.ts +0 -1
  160. package/dist/lib/agent-constants.js +0 -1
  161. package/dist/lib/agent-detection.d.ts +0 -1
  162. package/dist/lib/agent-detection.js +0 -1
  163. package/dist/lib/agent-execution.d.ts +0 -1
  164. package/dist/lib/agent-execution.js +0 -1
  165. package/dist/lib/agent-logs.d.ts +0 -1
  166. package/dist/lib/agent-logs.js +0 -1
  167. package/dist/lib/agent-preflight.d.ts +0 -1
  168. package/dist/lib/agent-preflight.js +0 -1
  169. package/dist/lib/agent-render.d.ts +0 -1
  170. package/dist/lib/agent-render.js +0 -1
  171. package/dist/lib/agent-shells.d.ts +0 -1
  172. package/dist/lib/agent-shells.js +0 -1
  173. package/dist/lib/agent-status.d.ts +0 -1
  174. package/dist/lib/agent-status.js +0 -1
  175. package/dist/lib/agent-types.d.ts +0 -1
  176. package/dist/lib/agent-types.js +0 -1
  177. package/dist/lib/agents.d.ts +0 -1
  178. package/dist/lib/agents.js +0 -1
  179. package/dist/lib/builtin-compiled-workflow.d.ts +0 -1
  180. package/dist/lib/builtin-compiled-workflow.js +0 -1
  181. package/dist/lib/chart-guidance.d.ts +0 -1
  182. package/dist/lib/chart-guidance.js +0 -1
  183. package/dist/lib/compiled-compile.d.ts +0 -1
  184. package/dist/lib/compiled-compile.js +0 -1
  185. package/dist/lib/compiled-paths.d.ts +0 -1
  186. package/dist/lib/compiled-paths.js +0 -3
  187. package/dist/lib/compiled-raw.d.ts +0 -1
  188. package/dist/lib/compiled-raw.js +0 -3
  189. package/dist/lib/compiled-reset.d.ts +0 -1
  190. package/dist/lib/compiled-reset.js +0 -3
  191. package/dist/lib/compiled-schema.d.ts +0 -1
  192. package/dist/lib/compiled-schema.js +0 -1
  193. package/dist/lib/discovery.d.ts +0 -1
  194. package/dist/lib/discovery.js +0 -1
  195. package/dist/lib/execution-profile.d.ts +0 -1
  196. package/dist/lib/execution-profile.js +0 -1
  197. package/dist/lib/executors.d.ts +0 -1
  198. package/dist/lib/executors.js +0 -1
  199. package/dist/lib/filesystem.d.ts +0 -1
  200. package/dist/lib/filesystem.js +0 -1
  201. package/dist/lib/interf-bootstrap.d.ts +0 -1
  202. package/dist/lib/interf-bootstrap.js +0 -3
  203. package/dist/lib/interf-detect.d.ts +0 -1
  204. package/dist/lib/interf-detect.js +0 -3
  205. package/dist/lib/interf-scaffold.d.ts +0 -1
  206. package/dist/lib/interf-scaffold.js +0 -3
  207. package/dist/lib/interf-workflow-package.d.ts +0 -1
  208. package/dist/lib/interf-workflow-package.js +0 -1
  209. package/dist/lib/interf.d.ts +0 -1
  210. package/dist/lib/interf.js +0 -3
  211. package/dist/lib/local-workflows.d.ts +0 -1
  212. package/dist/lib/local-workflows.js +0 -1
  213. package/dist/lib/logger.d.ts +0 -1
  214. package/dist/lib/logger.js +0 -1
  215. package/dist/lib/package-root.d.ts +0 -1
  216. package/dist/lib/package-root.js +0 -1
  217. package/dist/lib/parse.d.ts +0 -1
  218. package/dist/lib/parse.js +0 -1
  219. package/dist/lib/project-paths.d.ts +0 -1
  220. package/dist/lib/project-paths.js +0 -3
  221. package/dist/lib/runtime-acceptance.d.ts +0 -1
  222. package/dist/lib/runtime-acceptance.js +0 -1
  223. package/dist/lib/runtime-contracts.d.ts +0 -1
  224. package/dist/lib/runtime-contracts.js +0 -1
  225. package/dist/lib/runtime-inventory.d.ts +0 -1
  226. package/dist/lib/runtime-inventory.js +0 -1
  227. package/dist/lib/runtime-paths.d.ts +0 -1
  228. package/dist/lib/runtime-paths.js +0 -1
  229. package/dist/lib/runtime-prompt.d.ts +0 -1
  230. package/dist/lib/runtime-prompt.js +0 -1
  231. package/dist/lib/runtime-reconcile.d.ts +0 -1
  232. package/dist/lib/runtime-reconcile.js +0 -1
  233. package/dist/lib/runtime-runs.d.ts +0 -1
  234. package/dist/lib/runtime-runs.js +0 -1
  235. package/dist/lib/runtime-types.d.ts +0 -1
  236. package/dist/lib/runtime-types.js +0 -1
  237. package/dist/lib/runtime.d.ts +0 -1
  238. package/dist/lib/runtime.js +0 -1
  239. package/dist/lib/schema.d.ts +0 -4
  240. package/dist/lib/schema.js +0 -6
  241. package/dist/lib/source-config.d.ts +0 -1
  242. package/dist/lib/source-config.js +0 -3
  243. package/dist/lib/state-artifacts.d.ts +0 -1
  244. package/dist/lib/state-artifacts.js +0 -1
  245. package/dist/lib/state-health.d.ts +0 -1
  246. package/dist/lib/state-health.js +0 -1
  247. package/dist/lib/state-io.d.ts +0 -1
  248. package/dist/lib/state-io.js +0 -1
  249. package/dist/lib/state-paths.d.ts +0 -1
  250. package/dist/lib/state-paths.js +0 -1
  251. package/dist/lib/state-view.d.ts +0 -1
  252. package/dist/lib/state-view.js +0 -1
  253. package/dist/lib/state.d.ts +0 -1
  254. package/dist/lib/state.js +0 -1
  255. package/dist/lib/test-execution.d.ts +0 -1
  256. package/dist/lib/test-execution.js +0 -3
  257. package/dist/lib/test-matrices.d.ts +0 -1
  258. package/dist/lib/test-matrices.js +0 -3
  259. package/dist/lib/test-paths.d.ts +0 -1
  260. package/dist/lib/test-paths.js +0 -3
  261. package/dist/lib/test-profile-presets.d.ts +0 -1
  262. package/dist/lib/test-profile-presets.js +0 -3
  263. package/dist/lib/test-sandbox.d.ts +0 -1
  264. package/dist/lib/test-sandbox.js +0 -3
  265. package/dist/lib/test-specs.d.ts +0 -1
  266. package/dist/lib/test-specs.js +0 -3
  267. package/dist/lib/test-targets.d.ts +0 -1
  268. package/dist/lib/test-targets.js +0 -3
  269. package/dist/lib/test-types.d.ts +0 -1
  270. package/dist/lib/test-types.js +0 -3
  271. package/dist/lib/test.d.ts +0 -1
  272. package/dist/lib/test.js +0 -3
  273. package/dist/lib/util.d.ts +0 -1
  274. package/dist/lib/util.js +0 -1
  275. package/dist/lib/validate-compiled.d.ts +0 -1
  276. package/dist/lib/validate-compiled.js +0 -1
  277. package/dist/lib/validate-helpers.d.ts +0 -1
  278. package/dist/lib/validate-helpers.js +0 -1
  279. package/dist/lib/validate.d.ts +0 -1
  280. package/dist/lib/validate.js +0 -1
  281. package/dist/lib/workflow-authoring.d.ts +0 -1
  282. package/dist/lib/workflow-authoring.js +0 -1
  283. package/dist/lib/workflow-definitions.d.ts +0 -1
  284. package/dist/lib/workflow-definitions.js +0 -1
  285. package/dist/lib/workflow-edit-session.d.ts +0 -1
  286. package/dist/lib/workflow-edit-session.js +0 -1
  287. package/dist/lib/workflow-edit-utils.d.ts +0 -1
  288. package/dist/lib/workflow-edit-utils.js +0 -1
  289. package/dist/lib/workflow-helpers.d.ts +0 -1
  290. package/dist/lib/workflow-helpers.js +0 -1
  291. package/dist/lib/workflow-improvement.d.ts +0 -1
  292. package/dist/lib/workflow-improvement.js +0 -1
  293. package/dist/lib/workflow-primitives.d.ts +0 -1
  294. package/dist/lib/workflow-primitives.js +0 -1
  295. package/dist/lib/workflow-review-paths.d.ts +0 -1
  296. package/dist/lib/workflow-review-paths.js +0 -1
  297. package/dist/lib/workflow-stage-policy.d.ts +0 -1
  298. package/dist/lib/workflow-stage-policy.js +0 -1
  299. package/dist/lib/workflow-stage-runner.d.ts +0 -1
  300. package/dist/lib/workflow-stage-runner.js +0 -1
  301. package/dist/lib/workflows.d.ts +0 -1
  302. package/dist/lib/workflows.js +0 -1
@@ -0,0 +1,2841 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync, } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { CompileRunSchema, } from "../execution/lib/schema.js";
4
+ import { createRunEventId, createRunEventTimestamp, } from "../execution/events.js";
5
+ import { compiledRuntimeRunHistoryPath, compiledRuntimeRoot, testRootForCompiled, } from "../compiler/compiled-paths.js";
6
+ import { loadState, } from "../compiler/state.js";
7
+ import { ReadinessStateSchema, RuntimeRunSchema, } from "../compiler/lib/schema.js";
8
+ import { discoverSourceFiles, } from "../compiler/discovery.js";
9
+ import { ensurePortableContextScaffold, readInterfConfig, } from "../project-model/interf.js";
10
+ import { findSourceDatasetConfig, fingerprintReadinessChecks, listSourceDatasetConfigs, loadSourceFolderConfig, methodIdForSourcePreparationConfig, resolveConfiguredSourceFolderPath, resolveSourceDatasetPath, syncCompiledInterfConfigFromSourceDatasetConfig, upsertSourceDatasetConfig, } from "../project-model/source-config.js";
11
+ import { defaultDatasetNameForPath, listSourceFolderChoices, normalizeSourceDatasetPathForConfig, } from "../project-model/source-folders.js";
12
+ import { datasetLatestTestStatePath, portableContextPath, } from "../project-model/project-paths.js";
13
+ import { getCompiledWorkflow, listCompiledWorkflowChoices, } from "../workflow-package/workflow-definitions.js";
14
+ import { resolveWorkflowPackageSourcePath, } from "../workflow-package/local-workflows.js";
15
+ import { resolveAgent, detectAgents, supportsAutomatedRuns, } from "../agents/lib/detection.js";
16
+ import { AGENTS, } from "../agents/lib/constants.js";
17
+ import { loadUserConfig, saveUserConfig, } from "../agents/lib/user-config.js";
18
+ import { TestRunComparisonSchema, } from "../testing/lib/schema.js";
19
+ import { createCompiledTestTarget, } from "../testing/test-targets.js";
20
+ import { ActionProposalApprovalRequestSchema, ActionProposalCreateRequestSchema, ActionProposalPlanSchema, ActionProposalResourceSchema, CompileRunCreateRequestSchema, CompileRunResourceSchema, DatasetSetupCreateRequestSchema, DatasetResourceSchema, MethodResourceSchema, LocalExecutorStatusSchema, LocalExecutorSelectRequestSchema, LocalServiceHealthSchema, PreparationReadinessStateSchema, LocalRunHandlerResultSchema, LocalJobEventAppendRequestSchema, LocalJobRunCreateRequestSchema, LocalJobRunResourceSchema, SourceFileResourceSchema, WorkspaceFileResourceSchema, PortableContextResourceSchema, PreparationResourceSchema, ReadinessCheckDraftCreateRequestSchema, ReadinessCheckDraftResultSchema, RunObservabilityResourceSchema, TestRunCreateRequestSchema, TestRunResourceSchema, WorkflowAuthoringCreateRequestSchema, WorkflowAuthoringResultSchema, WorkflowPackageResourceSchema, } from "./lib/schema.js";
21
+ import { buildLocalServiceUrl, } from "./routes.js";
22
+ import { methodAuthoringTaskPrompt, MethodAuthoringActionValuesSchema, PreparationSetupActionValuesSchema, } from "./action-values.js";
23
+ function createRunId(prefix) {
24
+ return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
25
+ }
26
+ function createActionProposalId() {
27
+ return `action_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
28
+ }
29
+ const ACTION_PLANNER_CLARIFICATION_MESSAGE = "I can help with this Interf Workspace. Ask a question about Interf, or ask me to create a Preparation, prepare, check readiness, improve, or draft a Method and I will prepare an approval proposal.";
30
+ function readJsonFile(filePath) {
31
+ try {
32
+ return JSON.parse(readFileSync(filePath, "utf8"));
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ function writeJsonFile(filePath, value) {
39
+ mkdirSync(dirname(filePath), { recursive: true });
40
+ writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
41
+ }
42
+ function compileRunsRoot(compiledPath) {
43
+ return join(compiledRuntimeRoot(compiledPath), "compile-runs");
44
+ }
45
+ function compileRunPath(compiledPath, runId) {
46
+ return join(compileRunsRoot(compiledPath), `${runId}.json`);
47
+ }
48
+ function testRunsRoot(compiledPath) {
49
+ return join(testRootForCompiled(compiledPath), "service-runs");
50
+ }
51
+ function testRunPath(compiledPath, runId) {
52
+ return join(testRunsRoot(compiledPath), `${runId}.json`);
53
+ }
54
+ function localJobsRoot(rootPath) {
55
+ return join(rootPath, "interf", ".service", "jobs");
56
+ }
57
+ function localJobPath(rootPath, runId) {
58
+ return join(localJobsRoot(rootPath), `${runId}.json`);
59
+ }
60
+ function actionProposalsRoot(rootPath) {
61
+ return join(rootPath, "interf", ".service", "action-proposals");
62
+ }
63
+ function actionProposalPath(rootPath, proposalId) {
64
+ return join(actionProposalsRoot(rootPath), `${proposalId}.json`);
65
+ }
66
+ function listJsonFiles(dirPath) {
67
+ if (!existsSync(dirPath))
68
+ return [];
69
+ try {
70
+ if (!statSync(dirPath).isDirectory())
71
+ return [];
72
+ }
73
+ catch {
74
+ return [];
75
+ }
76
+ return readdirSync(dirPath)
77
+ .filter((entry) => entry.endsWith(".json"))
78
+ .map((entry) => join(dirPath, entry));
79
+ }
80
+ function readCompileRunAt(filePath) {
81
+ const parsed = CompileRunSchema.safeParse(readJsonFile(filePath));
82
+ return parsed.success ? parsed.data : null;
83
+ }
84
+ function readRuntimeRunHistory(compiledPath) {
85
+ const historyPath = compiledRuntimeRunHistoryPath(compiledPath);
86
+ if (!existsSync(historyPath))
87
+ return [];
88
+ try {
89
+ return readFileSync(historyPath, "utf8")
90
+ .split(/\r?\n/)
91
+ .map((line) => line.trim())
92
+ .filter((line) => line.length > 0)
93
+ .map((line) => {
94
+ try {
95
+ return RuntimeRunSchema.safeParse(JSON.parse(line));
96
+ }
97
+ catch {
98
+ return { success: false };
99
+ }
100
+ })
101
+ .filter((parsed) => parsed.success)
102
+ .map((parsed) => parsed.data);
103
+ }
104
+ catch {
105
+ return [];
106
+ }
107
+ }
108
+ function readTestRunAt(filePath) {
109
+ const parsed = TestRunResourceSchema.safeParse(readJsonFile(filePath));
110
+ return parsed.success ? parsed.data : null;
111
+ }
112
+ function readLocalJobRunAt(filePath) {
113
+ const parsed = LocalJobRunResourceSchema.safeParse(readJsonFile(filePath));
114
+ return parsed.success ? parsed.data : null;
115
+ }
116
+ function readActionProposalAt(filePath) {
117
+ const parsed = ActionProposalResourceSchema.safeParse(readJsonFile(filePath));
118
+ return parsed.success ? parsed.data : null;
119
+ }
120
+ function newestFirst(items) {
121
+ return [...items].sort((left, right) => {
122
+ const leftTime = Date.parse(left.started_at ?? left.finished_at ?? "");
123
+ const rightTime = Date.parse(right.started_at ?? right.finished_at ?? "");
124
+ return (Number.isFinite(rightTime) ? rightTime : 0) - (Number.isFinite(leftTime) ? leftTime : 0);
125
+ });
126
+ }
127
+ function newestJobFirst(items) {
128
+ return [...items].sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at));
129
+ }
130
+ function newestActionProposalFirst(items) {
131
+ return [...items].sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at));
132
+ }
133
+ function newestCompileFirst(items) {
134
+ return [...items].sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at));
135
+ }
136
+ function configuredAgentName() {
137
+ const config = loadUserConfig();
138
+ if (!config)
139
+ return null;
140
+ const configured = AGENTS.find((agent) => agent.command === config.agentCommand) ??
141
+ AGENTS.find((agent) => agent.name === config.agent) ??
142
+ AGENTS.find((agent) => agent.displayName === config.agent);
143
+ return configured?.name ?? null;
144
+ }
145
+ function detectedExecutorOptions(currentAgentName) {
146
+ return detectAgents()
147
+ .filter(supportsAutomatedRuns)
148
+ .map((agent) => ({
149
+ name: agent.name,
150
+ display_name: agent.displayName,
151
+ command: agent.command,
152
+ current: agent.name === currentAgentName,
153
+ }));
154
+ }
155
+ function uniqueArtifacts(artifacts) {
156
+ const seen = new Set();
157
+ const result = [];
158
+ for (const artifact of artifacts) {
159
+ const key = `${artifact.role}:${artifact.stage_id ?? ""}:${artifact.path}`;
160
+ if (seen.has(key))
161
+ continue;
162
+ seen.add(key);
163
+ result.push(artifact);
164
+ }
165
+ return result;
166
+ }
167
+ function observableRunStatus(status) {
168
+ if (status === "queued" || status === "pending")
169
+ return "queued";
170
+ if (status === "running")
171
+ return "running";
172
+ if (status === "failed")
173
+ return "failed";
174
+ if (status === "cancelled")
175
+ return "cancelled";
176
+ return "succeeded";
177
+ }
178
+ function workflowTraceRunStatus(status) {
179
+ if (status === "queued" || status === "pending")
180
+ return "pending";
181
+ if (status === "running")
182
+ return "running";
183
+ if (status === "failed")
184
+ return "failed";
185
+ if (status === "cancelled")
186
+ return "cancelled";
187
+ return "completed";
188
+ }
189
+ function safeWorkflowNamePart(value) {
190
+ return value
191
+ .replace(/[^a-zA-Z0-9._/-]+/g, "-")
192
+ .replace(/\/+/g, "/")
193
+ .replace(/^-+|-+$/g, "")
194
+ || "run";
195
+ }
196
+ function workflowTraceName(runType, name) {
197
+ return `workflow//./interf/${runType}//${safeWorkflowNamePart(name)}`;
198
+ }
199
+ function workflowStepName(runType, label) {
200
+ return `step//./interf/${runType}//${safeWorkflowNamePart(label)}`;
201
+ }
202
+ function structuredWorkflowError(message) {
203
+ return {
204
+ message: message || "Run failed.",
205
+ code: "INTERF_RUN_ERROR",
206
+ };
207
+ }
208
+ function traceEvent(runId, eventType, createdAt, index, eventData, correlationId) {
209
+ return {
210
+ runId,
211
+ eventId: `${runId}:event:${index}`,
212
+ eventType,
213
+ specVersion: 1,
214
+ createdAt: createdAt ?? createRunEventTimestamp(),
215
+ ...(correlationId ? { correlationId } : {}),
216
+ ...(eventData === undefined ? {} : { eventData }),
217
+ };
218
+ }
219
+ function traceStreamChunk(runId, index, createdAt, text, data) {
220
+ return {
221
+ streamId: `${runId}:events`,
222
+ chunkId: `${runId}:events:${index}`,
223
+ index,
224
+ createdAt: createdAt ?? createRunEventTimestamp(),
225
+ text,
226
+ ...(data === undefined ? {} : { data }),
227
+ };
228
+ }
229
+ function buildTraceStreamChunks(runId, events) {
230
+ return events.map((event, index) => traceStreamChunk(runId, index, event.timestamp, JSON.stringify({
231
+ time: event.timestamp ?? null,
232
+ type: event.type,
233
+ scope: event.stage_id ?? event.step_id ?? event.type,
234
+ message: event.message ?? event.summary ?? event.error ?? event.type,
235
+ }), event));
236
+ }
237
+ function proofChecksForCompileRun(run) {
238
+ return run.stages.flatMap((stage) => (stage.latest_proof?.checks ?? []).map((check) => ({
239
+ ...check,
240
+ detail: check.detail ?? `${stage.stage_label ?? stage.stage_id} / ${stage.status}`,
241
+ })));
242
+ }
243
+ function agentLabelForJob(job) {
244
+ return job.agent?.display_name ?? job.agent?.name ?? null;
245
+ }
246
+ function jobTypeLabelForMetric(type) {
247
+ if (type === "readiness-check-draft")
248
+ return "Readiness-check draft";
249
+ if (type === "method-authoring")
250
+ return "Method draft";
251
+ if (type === "method-improvement")
252
+ return "Method improvement";
253
+ if (type === "compile")
254
+ return "Compile";
255
+ if (type === "test")
256
+ return "Readiness check";
257
+ return "Local job";
258
+ }
259
+ function stageArtifactRefs(stageId, artifacts) {
260
+ return (artifacts ?? []).map((path) => ({
261
+ path,
262
+ role: "output",
263
+ stage_id: stageId,
264
+ label: path,
265
+ }));
266
+ }
267
+ function proofForStage(options) {
268
+ return {
269
+ id: `${options.runId}-${options.stageId}-proof`,
270
+ run_id: options.runId,
271
+ stage_id: options.stageId,
272
+ generated_at: options.stageState.finished_at ?? new Date().toISOString(),
273
+ summary: options.summary ?? `${options.stageId} produced stage evidence.`,
274
+ files_processed: options.stageState.counts?.source_total,
275
+ artifacts: options.artifacts,
276
+ checks: [
277
+ {
278
+ id: `${options.stageId}-status`,
279
+ label: "stage completed",
280
+ ok: options.stageState.status === "succeeded",
281
+ ...(options.stageState.status === "succeeded"
282
+ ? {}
283
+ : { detail: options.stageState.summary ?? "Stage did not complete successfully." }),
284
+ },
285
+ {
286
+ id: `${options.stageId}-artifacts`,
287
+ label: "artifacts recorded",
288
+ ok: options.artifacts.length > 0,
289
+ ...(options.artifacts.length > 0
290
+ ? {}
291
+ : { detail: "No stage artifacts were recorded." }),
292
+ },
293
+ ],
294
+ };
295
+ }
296
+ function logsForStageRun(stageState) {
297
+ const runId = stageState?.run_id;
298
+ if (!runId)
299
+ return undefined;
300
+ return {
301
+ prompt_path: `.interf/runtime/logs/${runId}.prompt.txt`,
302
+ event_stream_path: `.interf/runtime/logs/${runId}.events.ndjson`,
303
+ status_path: `.interf/runtime/logs/${runId}.status.log`,
304
+ contract_path: `.interf/runtime/logs/${runId}.stage-contract.json`,
305
+ };
306
+ }
307
+ function logsForRuntimeRun(run) {
308
+ if (!run)
309
+ return undefined;
310
+ return {
311
+ ...(run.logs?.prompt_path ? { prompt_path: run.logs.prompt_path } : {}),
312
+ ...(run.logs?.event_stream_path ? { event_stream_path: run.logs.event_stream_path } : {}),
313
+ ...(run.logs?.status_path ? { status_path: run.logs.status_path } : {}),
314
+ contract_path: run.contract_path,
315
+ };
316
+ }
317
+ function stageTracePayload(stage) {
318
+ return {
319
+ status: stage.status,
320
+ ...(stage.summary ? { summary: stage.summary } : {}),
321
+ ...(stage.artifacts.length > 0 ? { artifacts: stage.artifacts } : {}),
322
+ ...(stage.logs ? { logs: stage.logs } : {}),
323
+ ...(stage.latest_proof ? { proof: stage.latest_proof } : {}),
324
+ };
325
+ }
326
+ function timestampKey(value) {
327
+ const parsed = Date.parse(value ?? "");
328
+ return Number.isFinite(parsed) ? parsed : 0;
329
+ }
330
+ function applyEventToCompileRun(run, event) {
331
+ const now = event.timestamp;
332
+ const stageFor = (stageId) => {
333
+ const existing = run.stages.find((stage) => stage.stage_id === stageId);
334
+ if (existing)
335
+ return existing;
336
+ const created = {
337
+ run_id: run.run_id,
338
+ stage_id: stageId,
339
+ status: "queued",
340
+ artifacts: [],
341
+ };
342
+ run.stages.push(created);
343
+ return created;
344
+ };
345
+ const updateStage = (stageId, patch) => {
346
+ const current = stageFor(stageId);
347
+ Object.assign(current, patch);
348
+ };
349
+ switch (event.type) {
350
+ case "run.started":
351
+ return {
352
+ ...run,
353
+ status: "running",
354
+ started_at: run.started_at ?? now,
355
+ events: [...run.events, event],
356
+ };
357
+ case "stage.started":
358
+ updateStage(event.stage_id, {
359
+ status: "running",
360
+ started_at: now,
361
+ stage_index: event.stage_index,
362
+ stage_total: event.stage_total,
363
+ });
364
+ break;
365
+ case "artifact.written": {
366
+ const stage = stageFor(event.stage_id);
367
+ updateStage(event.stage_id, {
368
+ artifacts: uniqueArtifacts([...(stage.artifacts ?? []), event.artifact]),
369
+ });
370
+ break;
371
+ }
372
+ case "proof.updated":
373
+ if (event.stage_id) {
374
+ updateStage(event.stage_id, {
375
+ latest_proof: event.proof,
376
+ });
377
+ }
378
+ return {
379
+ ...run,
380
+ latest_proof: event.proof,
381
+ events: [...run.events, event],
382
+ };
383
+ case "log.appended":
384
+ break;
385
+ case "stage.passed":
386
+ updateStage(event.stage_id, {
387
+ status: "succeeded",
388
+ finished_at: now,
389
+ summary: event.summary ?? null,
390
+ failure: null,
391
+ });
392
+ break;
393
+ case "stage.failed":
394
+ updateStage(event.stage_id, {
395
+ status: "failed",
396
+ finished_at: now,
397
+ summary: event.error,
398
+ failure: event.error,
399
+ });
400
+ break;
401
+ case "run.completed":
402
+ return {
403
+ ...run,
404
+ status: "succeeded",
405
+ finished_at: run.finished_at ?? now,
406
+ events: [...run.events, event],
407
+ };
408
+ case "run.failed":
409
+ return {
410
+ ...run,
411
+ status: "failed",
412
+ finished_at: run.finished_at ?? now,
413
+ events: [...run.events, event],
414
+ };
415
+ default:
416
+ break;
417
+ }
418
+ return {
419
+ ...run,
420
+ events: [...run.events, event],
421
+ };
422
+ }
423
+ function applyEventToLocalJob(run, event) {
424
+ const stepFor = (stepId) => {
425
+ const existing = run.steps.find((step) => step.id === stepId);
426
+ if (existing)
427
+ return existing;
428
+ const created = {
429
+ id: stepId,
430
+ label: stepId,
431
+ status: "queued",
432
+ };
433
+ run.steps.push(created);
434
+ return created;
435
+ };
436
+ const updateStep = (stepId, patch) => {
437
+ if (!stepId)
438
+ return;
439
+ Object.assign(stepFor(stepId), patch);
440
+ };
441
+ switch (event.type) {
442
+ case "job.started":
443
+ return {
444
+ ...run,
445
+ status: "running",
446
+ started_at: run.started_at ?? event.timestamp,
447
+ events: [...run.events, event],
448
+ };
449
+ case "step.started":
450
+ updateStep(event.step_id, {
451
+ status: "running",
452
+ started_at: event.timestamp,
453
+ ...(event.input ? { input: event.input } : {}),
454
+ });
455
+ break;
456
+ case "step.completed":
457
+ updateStep(event.step_id, {
458
+ status: "succeeded",
459
+ finished_at: event.timestamp,
460
+ summary: event.message ?? null,
461
+ ...(event.output ? { output: event.output } : {}),
462
+ });
463
+ break;
464
+ case "step.failed":
465
+ updateStep(event.step_id, {
466
+ status: "failed",
467
+ finished_at: event.timestamp,
468
+ summary: event.message ?? null,
469
+ ...(event.output ? { output: event.output } : {}),
470
+ });
471
+ break;
472
+ case "job.completed":
473
+ return {
474
+ ...run,
475
+ status: "succeeded",
476
+ finished_at: run.finished_at ?? event.timestamp,
477
+ events: [...run.events, event],
478
+ };
479
+ case "job.failed":
480
+ return {
481
+ ...run,
482
+ status: "failed",
483
+ finished_at: run.finished_at ?? event.timestamp,
484
+ error: event.message ?? "Job failed.",
485
+ events: [...run.events, event],
486
+ };
487
+ case "artifact.written":
488
+ case "log.appended":
489
+ break;
490
+ default:
491
+ break;
492
+ }
493
+ return {
494
+ ...run,
495
+ events: [...run.events, event],
496
+ };
497
+ }
498
+ function compileRunToObservability(run) {
499
+ const artifacts = uniqueArtifacts(run.stages.flatMap((stage) => stage.artifacts));
500
+ const proof = proofChecksForCompileRun(run);
501
+ const executor = run.stages.find((stage) => stage.executor)?.executor ?? null;
502
+ const traceStatus = workflowTraceRunStatus(run.status);
503
+ const workflowName = workflowTraceName("compile", run.method);
504
+ const completedAt = run.finished_at ?? (traceStatus === "completed" || traceStatus === "failed" || traceStatus === "cancelled"
505
+ ? run.started_at ?? run.created_at
506
+ : undefined);
507
+ let eventIndex = 0;
508
+ const events = [
509
+ traceEvent(run.run_id, "run_created", run.created_at, eventIndex++, {
510
+ deploymentId: "local-interf",
511
+ workflowName,
512
+ input: {
513
+ kind: "compile",
514
+ preparation: run.preparation,
515
+ method: run.method,
516
+ source_path: run.source_path,
517
+ },
518
+ executionContext: {
519
+ backend: run.backend,
520
+ executor,
521
+ },
522
+ }),
523
+ ];
524
+ if (run.started_at) {
525
+ events.push(traceEvent(run.run_id, "run_started", run.started_at, eventIndex++, {
526
+ workflowName,
527
+ input: {
528
+ preparation: run.preparation,
529
+ method: run.method,
530
+ },
531
+ executionContext: {
532
+ backend: run.backend,
533
+ executor,
534
+ },
535
+ }));
536
+ }
537
+ for (const stage of run.stages) {
538
+ const stepName = workflowStepName("compile", stage.stage_label ?? stage.stage_id);
539
+ const stepCreatedAt = stage.started_at ?? run.started_at ?? run.created_at;
540
+ events.push(traceEvent(run.run_id, "step_created", stepCreatedAt, eventIndex++, {
541
+ stepName,
542
+ input: {
543
+ stage_id: stage.stage_id,
544
+ stage_label: stage.stage_label ?? null,
545
+ stage_index: stage.stage_index ?? null,
546
+ stage_total: stage.stage_total ?? null,
547
+ reads: stage.contract?.reads ?? [],
548
+ writes: stage.contract?.writes ?? [],
549
+ acceptance: stage.contract?.acceptance ?? null,
550
+ executor: stage.executor ?? null,
551
+ },
552
+ }, stage.stage_id));
553
+ if (stage.started_at || stage.status !== "queued") {
554
+ events.push(traceEvent(run.run_id, "step_started", stage.started_at ?? stepCreatedAt, eventIndex++, {
555
+ attempt: 1,
556
+ }, stage.stage_id));
557
+ }
558
+ if (stage.status === "failed") {
559
+ events.push(traceEvent(run.run_id, "step_failed", stage.finished_at ?? run.finished_at ?? stepCreatedAt, eventIndex++, {
560
+ error: structuredWorkflowError(stage.failure ?? stage.summary),
561
+ output: stageTracePayload(stage),
562
+ }, stage.stage_id));
563
+ }
564
+ else if (stage.status === "succeeded" || stage.status === "skipped") {
565
+ events.push(traceEvent(run.run_id, "step_completed", stage.finished_at ?? run.finished_at ?? stepCreatedAt, eventIndex++, {
566
+ result: stageTracePayload(stage),
567
+ }, stage.stage_id));
568
+ }
569
+ }
570
+ if (traceStatus === "completed") {
571
+ events.push(traceEvent(run.run_id, "run_completed", completedAt, eventIndex++, {
572
+ output: {
573
+ portable_context_path: run.portable_context_path,
574
+ artifacts,
575
+ proof,
576
+ latest_proof: run.latest_proof ?? null,
577
+ },
578
+ }));
579
+ }
580
+ else if (traceStatus === "failed") {
581
+ events.push(traceEvent(run.run_id, "run_failed", completedAt, eventIndex++, {
582
+ error: structuredWorkflowError(run.stages.find((stage) => stage.failure)?.failure ?? run.latest_proof?.summary),
583
+ }));
584
+ }
585
+ else if (traceStatus === "cancelled") {
586
+ events.push(traceEvent(run.run_id, "run_cancelled", completedAt, eventIndex++));
587
+ }
588
+ return RunObservabilityResourceSchema.parse({
589
+ kind: "interf-run-observability",
590
+ version: 1,
591
+ run_id: run.run_id,
592
+ run_type: "compile",
593
+ title: `Prepare ${run.preparation}`,
594
+ status: observableRunStatus(run.status),
595
+ preparation: run.preparation,
596
+ method: run.method,
597
+ source_path: run.source_path,
598
+ output_path: run.portable_context_path,
599
+ executor,
600
+ agent_label: executor?.display_name ?? run.backend,
601
+ created_at: run.created_at,
602
+ started_at: run.started_at ?? null,
603
+ finished_at: run.finished_at ?? null,
604
+ metrics: [
605
+ { label: "Preparation", value: run.preparation },
606
+ { label: "Method", value: run.method },
607
+ { label: "Executor", value: executor?.display_name ?? run.backend },
608
+ { label: "Stages", value: `${run.stages.filter((stage) => stage.status === "succeeded").length}/${run.stages.length}` },
609
+ { label: "Proof", value: `${proof.filter((check) => check.ok).length}/${proof.length}` },
610
+ { label: "Artifacts", value: String(artifacts.length) },
611
+ ],
612
+ artifacts,
613
+ proof,
614
+ trace: {
615
+ run: {
616
+ runId: run.run_id,
617
+ status: traceStatus,
618
+ deploymentId: "local-interf",
619
+ workflowName,
620
+ specVersion: 1,
621
+ executionContext: {
622
+ product: "Interf",
623
+ run_type: "compile",
624
+ preparation: run.preparation,
625
+ method: run.method,
626
+ backend: run.backend,
627
+ executor,
628
+ },
629
+ input: {
630
+ preparation: run.preparation,
631
+ method: run.method,
632
+ source_path: run.source_path,
633
+ },
634
+ ...(traceStatus === "completed"
635
+ ? {
636
+ output: {
637
+ portable_context_path: run.portable_context_path,
638
+ artifacts,
639
+ proof,
640
+ },
641
+ }
642
+ : {}),
643
+ ...(traceStatus === "failed" ? { error: structuredWorkflowError(run.stages.find((stage) => stage.failure)?.failure) } : {}),
644
+ ...(run.started_at ? { startedAt: run.started_at } : {}),
645
+ ...(completedAt ? { completedAt } : {}),
646
+ createdAt: run.created_at,
647
+ updatedAt: completedAt ?? run.started_at ?? run.created_at,
648
+ },
649
+ events,
650
+ streams: buildTraceStreamChunks(run.run_id, run.events),
651
+ },
652
+ });
653
+ }
654
+ function testRunToObservability(run) {
655
+ const traceStatus = workflowTraceRunStatus(run.status);
656
+ const title = `Check readiness ${run.preparation}`;
657
+ const workflowName = workflowTraceName("test", title);
658
+ const completedAt = run.finished_at ?? (traceStatus === "completed" || traceStatus === "failed" || traceStatus === "cancelled"
659
+ ? run.started_at ?? new Date().toISOString()
660
+ : undefined);
661
+ const raw = run.comparison?.raw ?? null;
662
+ const compiled = run.comparison?.compiled ?? null;
663
+ const proofCandidates = [
664
+ raw
665
+ ? {
666
+ id: `${run.run_id}-source-files`,
667
+ label: "source files baseline",
668
+ ok: raw.ok ?? raw.passed_cases === raw.total_cases,
669
+ detail: `${raw.passed_cases}/${raw.total_cases} checks`,
670
+ }
671
+ : null,
672
+ compiled
673
+ ? {
674
+ id: `${run.run_id}-portable-context`,
675
+ label: "portable context",
676
+ ok: compiled.ok ?? compiled.passed_cases === compiled.total_cases,
677
+ detail: `${compiled.passed_cases}/${compiled.total_cases} checks`,
678
+ }
679
+ : null,
680
+ ];
681
+ const proof = proofCandidates.filter((value) => value !== null);
682
+ const artifacts = uniqueArtifacts([
683
+ ...(raw?.run_path ? [{ path: raw.run_path, role: "test", label: "source files readiness run" }] : []),
684
+ ...(compiled?.run_path ? [{ path: compiled.run_path, role: "test", label: "portable context readiness run" }] : []),
685
+ ]);
686
+ let eventIndex = 0;
687
+ const events = [
688
+ traceEvent(run.run_id, "run_created", run.started_at, eventIndex++, {
689
+ deploymentId: "local-interf",
690
+ workflowName,
691
+ input: {
692
+ kind: "test",
693
+ preparation: run.preparation,
694
+ mode: run.mode,
695
+ },
696
+ }),
697
+ ];
698
+ if (run.started_at) {
699
+ events.push(traceEvent(run.run_id, "run_started", run.started_at, eventIndex++, {
700
+ workflowName,
701
+ input: {
702
+ preparation: run.preparation,
703
+ mode: run.mode,
704
+ },
705
+ }));
706
+ }
707
+ const targetStepCandidates = [
708
+ raw
709
+ ? {
710
+ id: "source-files-baseline",
711
+ label: "Source files baseline",
712
+ status: raw.ok ?? raw.passed_cases === raw.total_cases ? "succeeded" : "failed",
713
+ input: raw.target ?? { type: "source-files", path: run.source_path ?? null },
714
+ output: raw,
715
+ }
716
+ : null,
717
+ compiled
718
+ ? {
719
+ id: "portable-context-check",
720
+ label: "Portable context",
721
+ status: compiled.ok ?? compiled.passed_cases === compiled.total_cases ? "succeeded" : "failed",
722
+ input: compiled.target ?? { type: "portable-context", path: run.portable_context_path ?? null },
723
+ output: compiled,
724
+ }
725
+ : null,
726
+ ];
727
+ const targetSteps = targetStepCandidates.filter((value) => value !== null);
728
+ if (targetSteps.length === 0) {
729
+ targetSteps.push({
730
+ id: "check-readiness",
731
+ label: "Run readiness checks",
732
+ status: run.status === "failed" ? "failed" : "succeeded",
733
+ input: {
734
+ preparation: run.preparation,
735
+ mode: run.mode,
736
+ },
737
+ output: run.comparison ?? null,
738
+ });
739
+ }
740
+ for (const step of targetSteps) {
741
+ const timestamp = run.started_at ?? run.finished_at ?? new Date().toISOString();
742
+ events.push(traceEvent(run.run_id, "step_created", timestamp, eventIndex++, {
743
+ stepName: workflowStepName("test", step.label),
744
+ input: step.input,
745
+ }, step.id));
746
+ events.push(traceEvent(run.run_id, "step_started", timestamp, eventIndex++, {
747
+ attempt: 1,
748
+ }, step.id));
749
+ events.push(traceEvent(run.run_id, step.status === "failed" ? "step_failed" : "step_completed", run.finished_at ?? timestamp, eventIndex++, step.status === "failed"
750
+ ? { error: structuredWorkflowError(run.error ?? `${step.label} did not pass.`), output: step.output }
751
+ : { result: step.output }, step.id));
752
+ }
753
+ if (traceStatus === "completed") {
754
+ events.push(traceEvent(run.run_id, "run_completed", completedAt, eventIndex++, {
755
+ output: {
756
+ comparison: run.comparison,
757
+ proof,
758
+ },
759
+ }));
760
+ }
761
+ else if (traceStatus === "failed") {
762
+ events.push(traceEvent(run.run_id, "run_failed", completedAt, eventIndex++, {
763
+ error: structuredWorkflowError(run.error),
764
+ }));
765
+ }
766
+ else if (traceStatus === "cancelled") {
767
+ events.push(traceEvent(run.run_id, "run_cancelled", completedAt, eventIndex++));
768
+ }
769
+ return RunObservabilityResourceSchema.parse({
770
+ kind: "interf-run-observability",
771
+ version: 1,
772
+ run_id: run.run_id,
773
+ run_type: "test",
774
+ title,
775
+ status: observableRunStatus(run.status),
776
+ preparation: run.preparation,
777
+ method: compiled?.target?.workflow ?? null,
778
+ source_path: run.source_path ?? null,
779
+ output_path: run.portable_context_path ?? null,
780
+ created_at: run.started_at ?? completedAt ?? new Date().toISOString(),
781
+ started_at: run.started_at ?? null,
782
+ finished_at: run.finished_at ?? null,
783
+ metrics: [
784
+ { label: "Preparation", value: run.preparation },
785
+ { label: "Mode", value: run.mode },
786
+ { label: "Readiness checks", value: compiled ? `${compiled.passed_cases}/${compiled.total_cases}` : raw ? `${raw.passed_cases}/${raw.total_cases}` : "0/0" },
787
+ { label: "Source files", value: raw ? `${raw.passed_cases}/${raw.total_cases}` : "-" },
788
+ { label: "Portable context", value: compiled ? `${compiled.passed_cases}/${compiled.total_cases}` : "-" },
789
+ ],
790
+ artifacts,
791
+ proof,
792
+ trace: {
793
+ run: {
794
+ runId: run.run_id,
795
+ status: traceStatus,
796
+ deploymentId: "local-interf",
797
+ workflowName,
798
+ specVersion: 1,
799
+ executionContext: {
800
+ product: "Interf",
801
+ run_type: "test",
802
+ preparation: run.preparation,
803
+ mode: run.mode,
804
+ },
805
+ input: {
806
+ preparation: run.preparation,
807
+ mode: run.mode,
808
+ source_path: run.source_path ?? null,
809
+ portable_context_path: run.portable_context_path ?? null,
810
+ },
811
+ ...(traceStatus === "completed" ? { output: { comparison: run.comparison, proof } } : {}),
812
+ ...(traceStatus === "failed" ? { error: structuredWorkflowError(run.error) } : {}),
813
+ ...(run.started_at ? { startedAt: run.started_at } : {}),
814
+ ...(completedAt ? { completedAt } : {}),
815
+ createdAt: run.started_at ?? completedAt ?? new Date().toISOString(),
816
+ updatedAt: completedAt ?? run.started_at ?? new Date().toISOString(),
817
+ },
818
+ events,
819
+ streams: buildTraceStreamChunks(run.run_id, run.events ?? []),
820
+ },
821
+ });
822
+ }
823
+ function jobRunToObservability(job) {
824
+ const runType = (job.job_type === "preparation-setup" ||
825
+ job.job_type === "readiness-check-draft" ||
826
+ job.job_type === "method-authoring" ||
827
+ job.job_type === "method-improvement"
828
+ ? job.job_type
829
+ : "job");
830
+ const executor = job.agent
831
+ ? {
832
+ kind: "local-agent",
833
+ name: job.agent.name,
834
+ display_name: job.agent.display_name,
835
+ command: job.agent.command ?? null,
836
+ }
837
+ : null;
838
+ const traceStatus = workflowTraceRunStatus(job.status);
839
+ const workflowName = workflowTraceName(runType, job.title);
840
+ const completedAt = job.finished_at ?? (traceStatus === "completed" || traceStatus === "failed" || traceStatus === "cancelled"
841
+ ? job.started_at ?? job.created_at
842
+ : undefined);
843
+ let eventIndex = 0;
844
+ const events = [
845
+ traceEvent(job.run_id, "run_created", job.created_at, eventIndex++, {
846
+ deploymentId: "local-interf",
847
+ workflowName,
848
+ input: {
849
+ job_type: job.job_type,
850
+ preparation: job.preparation ?? null,
851
+ method: job.method ?? null,
852
+ },
853
+ executionContext: {
854
+ executor,
855
+ },
856
+ }),
857
+ ];
858
+ if (job.started_at) {
859
+ events.push(traceEvent(job.run_id, "run_started", job.started_at, eventIndex++, {
860
+ workflowName,
861
+ input: {
862
+ job_type: job.job_type,
863
+ preparation: job.preparation ?? null,
864
+ },
865
+ executionContext: {
866
+ executor,
867
+ },
868
+ }));
869
+ }
870
+ for (const step of job.steps) {
871
+ const timestamp = step.started_at ?? job.started_at ?? job.created_at;
872
+ events.push(traceEvent(job.run_id, "step_created", timestamp, eventIndex++, {
873
+ stepName: workflowStepName(runType, step.label),
874
+ input: step.input ?? {
875
+ step_id: step.id,
876
+ label: step.label,
877
+ },
878
+ }, step.id));
879
+ if (step.started_at || step.status !== "queued") {
880
+ events.push(traceEvent(job.run_id, "step_started", step.started_at ?? timestamp, eventIndex++, {
881
+ attempt: 1,
882
+ }, step.id));
883
+ }
884
+ if (step.status === "failed") {
885
+ events.push(traceEvent(job.run_id, "step_failed", step.finished_at ?? completedAt ?? timestamp, eventIndex++, {
886
+ error: structuredWorkflowError(step.summary ?? job.error),
887
+ output: step.output ?? null,
888
+ }, step.id));
889
+ }
890
+ else if (step.status === "succeeded") {
891
+ events.push(traceEvent(job.run_id, "step_completed", step.finished_at ?? completedAt ?? timestamp, eventIndex++, {
892
+ result: step.output ?? {
893
+ summary: step.summary ?? null,
894
+ },
895
+ }, step.id));
896
+ }
897
+ }
898
+ if (traceStatus === "completed") {
899
+ events.push(traceEvent(job.run_id, "run_completed", completedAt, eventIndex++, {
900
+ output: job.result ?? {
901
+ output_path: job.output_path ?? null,
902
+ },
903
+ }));
904
+ }
905
+ else if (traceStatus === "failed") {
906
+ events.push(traceEvent(job.run_id, "run_failed", completedAt, eventIndex++, {
907
+ error: structuredWorkflowError(job.error),
908
+ }));
909
+ }
910
+ else if (traceStatus === "cancelled") {
911
+ events.push(traceEvent(job.run_id, "run_cancelled", completedAt, eventIndex++));
912
+ }
913
+ const outputPath = job.output_path ?? (typeof job.result?.path === "string" ? job.result.path : null);
914
+ const artifacts = uniqueArtifacts(outputPath ? [{ path: outputPath, role: "output" }] : []);
915
+ return RunObservabilityResourceSchema.parse({
916
+ kind: "interf-run-observability",
917
+ version: 1,
918
+ run_id: job.run_id,
919
+ run_type: runType,
920
+ title: job.title,
921
+ status: observableRunStatus(job.status),
922
+ preparation: job.preparation ?? null,
923
+ method: job.method ?? null,
924
+ source_path: job.source_path ?? null,
925
+ output_path: outputPath,
926
+ executor,
927
+ agent_label: agentLabelForJob(job),
928
+ created_at: job.created_at,
929
+ started_at: job.started_at ?? null,
930
+ finished_at: job.finished_at ?? null,
931
+ metrics: [
932
+ { label: "Kind", value: jobTypeLabelForMetric(job.job_type) },
933
+ { label: "Executor", value: agentLabelForJob(job) ?? "local executor" },
934
+ { label: "Phases", value: `${job.steps.filter((step) => step.status === "succeeded").length}/${job.steps.length}` },
935
+ { label: "Preparation", value: job.preparation ?? "-" },
936
+ { label: "Method", value: job.method ?? "-" },
937
+ ],
938
+ artifacts,
939
+ proof: [],
940
+ trace: {
941
+ run: {
942
+ runId: job.run_id,
943
+ status: traceStatus,
944
+ deploymentId: "local-interf",
945
+ workflowName,
946
+ specVersion: 1,
947
+ executionContext: {
948
+ product: "Interf",
949
+ run_type: runType,
950
+ job_type: job.job_type,
951
+ preparation: job.preparation ?? null,
952
+ method: job.method ?? null,
953
+ executor,
954
+ },
955
+ input: {
956
+ job_type: job.job_type,
957
+ preparation: job.preparation ?? null,
958
+ method: job.method ?? null,
959
+ source_path: job.source_path ?? null,
960
+ },
961
+ ...(traceStatus === "completed" ? { output: job.result ?? { output_path: outputPath } } : {}),
962
+ ...(traceStatus === "failed" ? { error: structuredWorkflowError(job.error) } : {}),
963
+ ...(job.started_at ? { startedAt: job.started_at } : {}),
964
+ ...(completedAt ? { completedAt } : {}),
965
+ createdAt: job.created_at,
966
+ updatedAt: completedAt ?? job.started_at ?? job.created_at,
967
+ },
968
+ events,
969
+ streams: buildTraceStreamChunks(job.run_id, job.events),
970
+ },
971
+ });
972
+ }
973
+ function slugFromText(value) {
974
+ const slug = value
975
+ .toLowerCase()
976
+ .replace(/[^a-z0-9]+/g, "-")
977
+ .replace(/^-+|-+$/g, "")
978
+ .slice(0, 42)
979
+ .replace(/-+$/g, "");
980
+ return slug || "custom";
981
+ }
982
+ function stringValue(values, key) {
983
+ const value = values?.[key];
984
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
985
+ }
986
+ function numberValue(values, key) {
987
+ const value = values?.[key];
988
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
989
+ }
990
+ function testModeFromValues(values) {
991
+ const value = stringValue(values, "mode") ?? stringValue(values, "target");
992
+ return value === "raw" || value === "compiled" || value === "both" ? value : null;
993
+ }
994
+ function testModeValue(values, defaultMode = "both") {
995
+ return testModeFromValues(values) ?? defaultMode;
996
+ }
997
+ function workflowIdForProposal(message, values) {
998
+ const explicit = stringValue(values, "method_id") ??
999
+ stringValue(values, "method");
1000
+ return explicit ?? `custom-${slugFromText(message)}`;
1001
+ }
1002
+ function actionValueMethodTaskPrompt(values) {
1003
+ const parsed = MethodAuthoringActionValuesSchema.safeParse(values);
1004
+ return parsed.success ? methodAuthoringTaskPrompt(parsed.data) : null;
1005
+ }
1006
+ function datasetSetupPathValue(values) {
1007
+ return stringValue(values, "path") ??
1008
+ stringValue(values, "source_folder_path") ??
1009
+ stringValue(values, "source_path") ??
1010
+ stringValue(values, "source_folder") ??
1011
+ stringValue(values, "folder");
1012
+ }
1013
+ function datasetSetupNameValue(values) {
1014
+ const parsed = PreparationSetupActionValuesSchema.safeParse(values);
1015
+ return parsed.success ? parsed.data.name : stringValue(values, "name") ??
1016
+ stringValue(values, "preparation") ??
1017
+ stringValue(values, "preparation_name");
1018
+ }
1019
+ function actionCommandPreview(actionType, datasetName, workflowId, values) {
1020
+ if (actionType === "preparation-setup") {
1021
+ const datasetPart = datasetName ? ` # Preparation: ${datasetName}` : "";
1022
+ const pathPart = datasetSetupPathValue(values);
1023
+ return pathPart ? `interf init # source: ${pathPart}${datasetPart}` : `interf init${datasetPart}`;
1024
+ }
1025
+ if (actionType === "compile") {
1026
+ const workflowSuffix = workflowId ? ` # Method: ${workflowId}` : "";
1027
+ return datasetName
1028
+ ? `interf compile --preparation ${datasetName}${workflowSuffix}`
1029
+ : `interf compile${workflowSuffix}`;
1030
+ }
1031
+ if (actionType === "test") {
1032
+ const mode = testModeValue(values);
1033
+ return datasetName
1034
+ ? `interf test --preparation ${datasetName} --target ${mode}`
1035
+ : `interf test --target ${mode}`;
1036
+ }
1037
+ if (actionType === "readiness-check-draft") {
1038
+ return "interf # choose Auto-create readiness checks";
1039
+ }
1040
+ if (actionType === "method-authoring" || actionType === "method-improvement") {
1041
+ return "interf create method";
1042
+ }
1043
+ return "Try: create a Preparation, prepare, check readiness, draft readiness checks, or draft a Method.";
1044
+ }
1045
+ function hasCompiledTestTarget(sourcePath, datasetConfig) {
1046
+ const compiledPath = portableContextPath(sourcePath, datasetConfig.name);
1047
+ if (!existsSync(compiledPath))
1048
+ return false;
1049
+ return createCompiledTestTarget(compiledPath, datasetConfig.name, methodIdForSourcePreparationConfig(datasetConfig) ?? "interf").eligible;
1050
+ }
1051
+ function actionAssistantMessage(actionType, datasetName, commandPreview) {
1052
+ const datasetSuffix = datasetName ? ` for Preparation "${datasetName}"` : "";
1053
+ if (actionType === "preparation-setup") {
1054
+ return `Interf prepared a Preparation setup proposal${datasetSuffix}. Approve to save the source folder and create the portable-context scaffold. CLI equivalent: ${commandPreview}`;
1055
+ }
1056
+ if (actionType === "compile") {
1057
+ return `Interf prepared a prepare-run proposal${datasetSuffix}. Approve to submit it through the local Interf service and watch the run in Interf. CLI equivalent: ${commandPreview}`;
1058
+ }
1059
+ if (actionType === "test") {
1060
+ return `Interf prepared a readiness-check proposal${datasetSuffix}. Approve to run the requested target against saved readiness checks. CLI equivalent: ${commandPreview}`;
1061
+ }
1062
+ if (actionType === "readiness-check-draft") {
1063
+ return `Interf prepared a readiness-check draft proposal${datasetSuffix}. Approve to ask the configured local executor to draft checks as a visible run. CLI equivalent: ${commandPreview}`;
1064
+ }
1065
+ if (actionType === "method-authoring" || actionType === "method-improvement") {
1066
+ return `Interf prepared a Method draft proposal${datasetSuffix}. Approve to draft a reusable local Method as a visible run. CLI equivalent: ${commandPreview}`;
1067
+ }
1068
+ return "I could not map that to a safe Interf action. Ask for one action in plain English, such as create a Preparation, prepare the data, check readiness, draft readiness checks, or draft a Method.";
1069
+ }
1070
+ function passRate(passed, total) {
1071
+ if (total <= 0)
1072
+ return null;
1073
+ return Math.round((passed / total) * 100);
1074
+ }
1075
+ function readinessTargetResult(summary, currentFingerprint, comparisonFingerprint) {
1076
+ if (!summary)
1077
+ return null;
1078
+ const resultFingerprint = comparisonFingerprint ?? null;
1079
+ return {
1080
+ passed: summary.passed_cases,
1081
+ total: summary.total_cases,
1082
+ pass_rate: passRate(summary.passed_cases, summary.total_cases),
1083
+ checks_fingerprint: resultFingerprint,
1084
+ stale: Boolean(currentFingerprint && resultFingerprint && currentFingerprint !== resultFingerprint),
1085
+ run_id: null,
1086
+ run_path: summary.run_path,
1087
+ };
1088
+ }
1089
+ function readinessSummaryForStatus(status) {
1090
+ if (status === "ready")
1091
+ return "Ready for agent work.";
1092
+ if (status === "not-ready")
1093
+ return "Readiness checks did not pass.";
1094
+ if (status === "stale")
1095
+ return "Readiness checks are stale for the current saved checks.";
1096
+ if (status === "checking")
1097
+ return "Readiness checks are running.";
1098
+ if (status === "building")
1099
+ return "Portable context is building.";
1100
+ if (status === "built")
1101
+ return "Portable context is built; readiness has not been proven yet.";
1102
+ if (status === "not-built")
1103
+ return "Portable context has not been built yet.";
1104
+ if (status === "not-configured")
1105
+ return "No readiness checks are configured.";
1106
+ return "Latest preparation failed.";
1107
+ }
1108
+ function readinessStateToPreparationReadiness(readiness) {
1109
+ return PreparationReadinessStateSchema.parse({
1110
+ ...readiness,
1111
+ checks: readiness.checks.map((check) => ({ ...check })),
1112
+ });
1113
+ }
1114
+ function datasetResourceToPreparationResource(rootPath, resource) {
1115
+ const latestCompileRunId = resource.latest_compile_run_id ?? null;
1116
+ const latestTestRunId = resource.latest_test_run_id ?? null;
1117
+ const methodId = methodIdForSourcePreparationConfig(resource.dataset);
1118
+ return PreparationResourceSchema.parse({
1119
+ id: resource.dataset.name,
1120
+ name: resource.dataset.name,
1121
+ preparation: resource.dataset,
1122
+ source_path: resolveSourceDatasetPath(rootPath, resource.dataset),
1123
+ method_id: methodId,
1124
+ checks: resource.dataset.checks,
1125
+ portable_context: {
1126
+ preparation: resource.dataset.name,
1127
+ path: resource.portable_context_path,
1128
+ exists: resource.portable_context_path !== null,
1129
+ method_id: methodId,
1130
+ latest_compile_run_id: latestCompileRunId,
1131
+ latest_test_run_id: latestTestRunId,
1132
+ },
1133
+ portable_context_path: resource.portable_context_path,
1134
+ readiness: readinessStateToPreparationReadiness(resource.readiness),
1135
+ runs: {
1136
+ latest_compile_run_id: latestCompileRunId,
1137
+ latest_test_run_id: latestTestRunId,
1138
+ },
1139
+ latest_compile_run_id: latestCompileRunId,
1140
+ latest_test_run_id: latestTestRunId,
1141
+ });
1142
+ }
1143
+ function workflowPackageResourceToMethodResource(resource) {
1144
+ return MethodResourceSchema.parse({
1145
+ id: resource.id,
1146
+ method_id: resource.id,
1147
+ path: resource.path,
1148
+ ...(resource.label ? { label: resource.label } : {}),
1149
+ ...(resource.hint ? { hint: resource.hint } : {}),
1150
+ source_kind: resource.source_kind,
1151
+ built_in: resource.built_in,
1152
+ active_for_preparations: resource.active_for_datasets,
1153
+ stages: resource.stages,
1154
+ });
1155
+ }
1156
+ export class LocalServiceRuntime {
1157
+ rootPath;
1158
+ host;
1159
+ port;
1160
+ startedAt;
1161
+ packageVersion;
1162
+ handlers;
1163
+ constructor(options) {
1164
+ this.rootPath = resolve(options.rootPath);
1165
+ this.host = options.host;
1166
+ this.port = options.port;
1167
+ this.startedAt = options.startedAt ?? new Date().toISOString();
1168
+ this.packageVersion = options.packageVersion;
1169
+ this.handlers = options.handlers ?? {};
1170
+ }
1171
+ health() {
1172
+ const sourceFolderPath = resolveConfiguredSourceFolderPath(this.rootPath);
1173
+ return LocalServiceHealthSchema.parse({
1174
+ kind: "interf-local-service-health",
1175
+ version: 1,
1176
+ status: "ok",
1177
+ host: this.host,
1178
+ port: this.port,
1179
+ service_url: buildLocalServiceUrl({ host: this.host, port: this.port }),
1180
+ control_path: this.rootPath,
1181
+ source_folder_path: sourceFolderPath,
1182
+ started_at: this.startedAt,
1183
+ ...(this.packageVersion ? { package_version: this.packageVersion } : {}),
1184
+ });
1185
+ }
1186
+ listDatasets() {
1187
+ const config = loadSourceFolderConfig(this.rootPath);
1188
+ return listSourceDatasetConfigs(config).map((dataset) => {
1189
+ const compileRuns = this.listCompileRunsForDataset(dataset.name);
1190
+ const testRuns = this.listTestRunsForDataset(dataset.name);
1191
+ const path = portableContextPath(this.rootPath, dataset.name);
1192
+ const readiness = this.computeDatasetReadiness(dataset);
1193
+ return DatasetResourceSchema.parse({
1194
+ dataset,
1195
+ portable_context_path: existsSync(path) ? path : null,
1196
+ readiness,
1197
+ latest_compile_run_id: compileRuns[0]?.run_id ?? null,
1198
+ latest_test_run_id: testRuns[0]?.run_id ?? null,
1199
+ });
1200
+ });
1201
+ }
1202
+ getDataset(datasetName) {
1203
+ return this.listDatasets().find((dataset) => dataset.dataset.name === datasetName) ?? null;
1204
+ }
1205
+ listPreparations() {
1206
+ return this.listDatasets().map((resource) => datasetResourceToPreparationResource(this.rootPath, resource));
1207
+ }
1208
+ getPreparation(preparationName) {
1209
+ const dataset = this.getDataset(preparationName);
1210
+ return dataset ? datasetResourceToPreparationResource(this.rootPath, dataset) : null;
1211
+ }
1212
+ listPreparationReadiness() {
1213
+ return this.listReadiness().map(readinessStateToPreparationReadiness);
1214
+ }
1215
+ getPreparationReadiness(preparationName) {
1216
+ const readiness = this.getReadiness(preparationName);
1217
+ return readiness ? readinessStateToPreparationReadiness(readiness) : null;
1218
+ }
1219
+ listReadiness() {
1220
+ const config = loadSourceFolderConfig(this.rootPath);
1221
+ return listSourceDatasetConfigs(config).map((dataset) => this.computeDatasetReadiness(dataset));
1222
+ }
1223
+ getReadiness(datasetName) {
1224
+ const dataset = findSourceDatasetConfig(loadSourceFolderConfig(this.rootPath), datasetName);
1225
+ return dataset ? this.computeDatasetReadiness(dataset) : null;
1226
+ }
1227
+ computeDatasetReadiness(dataset) {
1228
+ const generatedAt = new Date().toISOString();
1229
+ const compiledPath = portableContextPath(this.rootPath, dataset.name);
1230
+ const contextExists = existsSync(compiledPath);
1231
+ const compileRun = this.listCompileRunsForDataset(dataset.name)[0] ?? null;
1232
+ const testRun = this.listTestRunsForDataset(dataset.name)[0] ?? null;
1233
+ const comparison = testRun?.comparison ?? this.readLatestComparison(dataset.name);
1234
+ const configuredChecks = dataset.checks.length;
1235
+ const currentFingerprint = configuredChecks > 0 ? fingerprintReadinessChecks(dataset.checks) : null;
1236
+ const comparisonFingerprint = comparison?.checks_fingerprint ?? null;
1237
+ const sourceResult = readinessTargetResult(comparison?.raw, currentFingerprint, comparisonFingerprint);
1238
+ const contextResult = readinessTargetResult(comparison?.compiled, currentFingerprint, comparisonFingerprint);
1239
+ const checksStale = Boolean(currentFingerprint && comparisonFingerprint && currentFingerprint !== comparisonFingerprint);
1240
+ const compileCheck = (() => {
1241
+ if (!compileRun) {
1242
+ return {
1243
+ gate: "compile-run",
1244
+ ok: false,
1245
+ status: "not-built",
1246
+ summary: "No compile run has built portable context yet.",
1247
+ artifact_path: contextExists ? compiledPath : null,
1248
+ };
1249
+ }
1250
+ if (compileRun.status === "succeeded") {
1251
+ return {
1252
+ gate: "compile-run",
1253
+ ok: contextExists,
1254
+ status: contextExists ? "built" : "failed",
1255
+ summary: contextExists
1256
+ ? "Latest compile run built portable context."
1257
+ : "Latest compile run finished, but portable context is missing.",
1258
+ run_id: compileRun.run_id,
1259
+ artifact_path: contextExists ? compiledPath : null,
1260
+ };
1261
+ }
1262
+ if (compileRun.status === "queued" || compileRun.status === "running") {
1263
+ return {
1264
+ gate: "compile-run",
1265
+ ok: false,
1266
+ status: "building",
1267
+ summary: "Latest compile run is still building portable context.",
1268
+ run_id: compileRun.run_id,
1269
+ artifact_path: contextExists ? compiledPath : null,
1270
+ };
1271
+ }
1272
+ return {
1273
+ gate: "compile-run",
1274
+ ok: false,
1275
+ status: "failed",
1276
+ summary: compileRun.status === "cancelled"
1277
+ ? "Latest compile run was cancelled."
1278
+ : "Latest compile run failed.",
1279
+ run_id: compileRun.run_id,
1280
+ artifact_path: contextExists ? compiledPath : null,
1281
+ };
1282
+ })();
1283
+ const checks = [
1284
+ {
1285
+ gate: "preparation-config",
1286
+ ok: true,
1287
+ summary: "Preparation is saved in this control plane folder.",
1288
+ },
1289
+ {
1290
+ gate: "portable-context",
1291
+ ok: contextExists,
1292
+ status: contextExists ? "built" : "not-built",
1293
+ summary: contextExists
1294
+ ? "Portable context folder exists."
1295
+ : "Portable context folder has not been created.",
1296
+ artifact_path: contextExists ? compiledPath : null,
1297
+ },
1298
+ compileCheck,
1299
+ {
1300
+ gate: "readiness-checks",
1301
+ ok: configuredChecks > 0,
1302
+ status: configuredChecks > 0 ? "built" : "not-configured",
1303
+ summary: configuredChecks > 0
1304
+ ? `${configuredChecks} readiness check${configuredChecks === 1 ? "" : "s"} configured.`
1305
+ : "No readiness checks are configured.",
1306
+ },
1307
+ {
1308
+ gate: "checks-current",
1309
+ ok: !checksStale,
1310
+ status: checksStale ? "stale" : "built",
1311
+ summary: checksStale
1312
+ ? "Latest readiness result is stale for the current saved checks."
1313
+ : "Latest readiness result matches the current saved checks.",
1314
+ },
1315
+ ];
1316
+ const status = (() => {
1317
+ if (compileRun?.status === "queued" || compileRun?.status === "running")
1318
+ return "building";
1319
+ if (testRun?.status === "queued" || testRun?.status === "running")
1320
+ return "checking";
1321
+ if (compileRun?.status === "failed" || compileRun?.status === "cancelled")
1322
+ return "failed";
1323
+ if (!compileRun || !contextExists)
1324
+ return "not-built";
1325
+ if (configuredChecks === 0)
1326
+ return "not-configured";
1327
+ if (checksStale)
1328
+ return "stale";
1329
+ if (!contextResult)
1330
+ return "built";
1331
+ return contextResult.total > 0 && contextResult.passed === contextResult.total ? "ready" : "not-ready";
1332
+ })();
1333
+ const ready = status === "ready";
1334
+ return ReadinessStateSchema.parse({
1335
+ kind: "interf-readiness-state",
1336
+ version: 1,
1337
+ generated_at: generatedAt,
1338
+ preparation: dataset.name,
1339
+ status,
1340
+ ready,
1341
+ summary: readinessSummaryForStatus(status),
1342
+ portable_context_path: contextExists ? compiledPath : null,
1343
+ latest_compile_run_id: compileRun?.run_id ?? null,
1344
+ latest_test_run_id: testRun?.run_id ?? null,
1345
+ compile: compileCheck,
1346
+ check_results: {
1347
+ configured: configuredChecks,
1348
+ fingerprint: currentFingerprint,
1349
+ source_files: sourceResult,
1350
+ portable_context: contextResult,
1351
+ delta: comparison?.summary.pass_rate_delta ?? null,
1352
+ },
1353
+ checks,
1354
+ });
1355
+ }
1356
+ listSourceFiles(datasetName) {
1357
+ const datasets = listSourceDatasetConfigs(loadSourceFolderConfig(this.rootPath))
1358
+ .filter((dataset) => !datasetName || dataset.name === datasetName);
1359
+ return datasets.flatMap((dataset) => {
1360
+ const sourceFolderPath = resolveSourceDatasetPath(this.rootPath, dataset);
1361
+ const compiledPath = portableContextPath(this.rootPath, dataset.name);
1362
+ return discoverSourceFiles(sourceFolderPath, compiledPath).sourceFiles.map((relativePath) => {
1363
+ const absolutePath = join(sourceFolderPath, relativePath);
1364
+ let sizeBytes = 0;
1365
+ let modifiedAt = null;
1366
+ try {
1367
+ const stat = statSync(absolutePath);
1368
+ sizeBytes = stat.size;
1369
+ modifiedAt = stat.mtime.toISOString();
1370
+ }
1371
+ catch {
1372
+ sizeBytes = 0;
1373
+ modifiedAt = null;
1374
+ }
1375
+ return SourceFileResourceSchema.parse({
1376
+ preparation: dataset.name,
1377
+ path: relativePath,
1378
+ absolute_path: absolutePath,
1379
+ size_bytes: sizeBytes,
1380
+ modified_at: modifiedAt,
1381
+ source_folder_path: sourceFolderPath,
1382
+ });
1383
+ });
1384
+ });
1385
+ }
1386
+ listWorkspaceFiles() {
1387
+ const sourceFolderPath = resolveConfiguredSourceFolderPath(this.rootPath) ?? this.rootPath;
1388
+ return discoverSourceFiles(sourceFolderPath, join(this.rootPath, "interf")).sourceFiles.map((relativePath) => {
1389
+ const absolutePath = join(sourceFolderPath, relativePath);
1390
+ let sizeBytes = 0;
1391
+ let modifiedAt = null;
1392
+ try {
1393
+ const stat = statSync(absolutePath);
1394
+ sizeBytes = stat.size;
1395
+ modifiedAt = stat.mtime.toISOString();
1396
+ }
1397
+ catch {
1398
+ sizeBytes = 0;
1399
+ modifiedAt = null;
1400
+ }
1401
+ return WorkspaceFileResourceSchema.parse({
1402
+ path: relativePath,
1403
+ absolute_path: absolutePath,
1404
+ size_bytes: sizeBytes,
1405
+ modified_at: modifiedAt,
1406
+ });
1407
+ });
1408
+ }
1409
+ listWorkflowPackages() {
1410
+ const datasets = listSourceDatasetConfigs(loadSourceFolderConfig(this.rootPath));
1411
+ const choices = listCompiledWorkflowChoices(this.rootPath);
1412
+ const hasLocalDefault = choices.some((workflow) => workflow.scope === "local" && workflow.id === "interf-default");
1413
+ const implicitDefaultWorkflowId = hasLocalDefault ? "interf-default" : "interf";
1414
+ return choices.filter((workflow) => !(hasLocalDefault && workflow.scope === "builtin" && workflow.id === "interf")).map((workflow) => {
1415
+ const activeForDatasets = datasets
1416
+ .filter((dataset) => (methodIdForSourcePreparationConfig(dataset) ?? implicitDefaultWorkflowId) === workflow.id)
1417
+ .map((dataset) => dataset.name);
1418
+ return WorkflowPackageResourceSchema.parse({
1419
+ id: workflow.id,
1420
+ path: resolveWorkflowPackageSourcePath(this.rootPath, workflow.id) ?? workflow.id,
1421
+ label: workflow.label,
1422
+ hint: workflow.hint,
1423
+ source_kind: workflow.scope === "builtin" ? "builtin" : "local",
1424
+ built_in: workflow.scope === "builtin",
1425
+ active_for_datasets: activeForDatasets,
1426
+ stages: workflow.stages.map((stage) => ({
1427
+ id: stage.id,
1428
+ label: stage.label,
1429
+ description: stage.description,
1430
+ contract_type: stage.contractType,
1431
+ skill_dir: stage.skillDir,
1432
+ reads: stage.reads,
1433
+ writes: stage.writes,
1434
+ ...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
1435
+ })),
1436
+ });
1437
+ });
1438
+ }
1439
+ getWorkflowPackage(workflowId) {
1440
+ return this.listWorkflowPackages().find((workflow) => workflow.id === workflowId) ?? null;
1441
+ }
1442
+ listMethods() {
1443
+ return this.listWorkflowPackages().map(workflowPackageResourceToMethodResource);
1444
+ }
1445
+ getMethod(methodId) {
1446
+ const workflowPackage = this.getWorkflowPackage(methodId);
1447
+ return workflowPackage ? workflowPackageResourceToMethodResource(workflowPackage) : null;
1448
+ }
1449
+ listJobs() {
1450
+ return newestJobFirst(listJsonFiles(localJobsRoot(this.rootPath))
1451
+ .map(readLocalJobRunAt)
1452
+ .filter((run) => run !== null));
1453
+ }
1454
+ getJob(runId) {
1455
+ return this.listJobs().find((run) => run.run_id === runId) ?? null;
1456
+ }
1457
+ getJobEvents(runId) {
1458
+ return this.getJob(runId)?.events ?? null;
1459
+ }
1460
+ getExecutorStatus() {
1461
+ const checkedAt = new Date().toISOString();
1462
+ try {
1463
+ const resolved = resolveAgent();
1464
+ const currentName = resolved.agent?.name ?? configuredAgentName();
1465
+ const availableExecutors = detectedExecutorOptions(currentName);
1466
+ if (!resolved.agent) {
1467
+ return LocalExecutorStatusSchema.parse({
1468
+ kind: "interf-local-executor-status",
1469
+ version: 1,
1470
+ status: "unavailable",
1471
+ checked_at: checkedAt,
1472
+ executor: null,
1473
+ available_executors: availableExecutors,
1474
+ message: resolved.error ?? "No supported local coding agent is configured.",
1475
+ });
1476
+ }
1477
+ return LocalExecutorStatusSchema.parse({
1478
+ kind: "interf-local-executor-status",
1479
+ version: 1,
1480
+ status: "connected",
1481
+ checked_at: checkedAt,
1482
+ executor: {
1483
+ name: resolved.agent.name,
1484
+ display_name: resolved.agent.displayName,
1485
+ command: resolved.agent.command,
1486
+ },
1487
+ available_executors: availableExecutors.length > 0
1488
+ ? availableExecutors
1489
+ : [{
1490
+ name: resolved.agent.name,
1491
+ display_name: resolved.agent.displayName,
1492
+ command: resolved.agent.command,
1493
+ current: true,
1494
+ }],
1495
+ message: `${resolved.agent.displayName} is configured for local Interf actions.`,
1496
+ });
1497
+ }
1498
+ catch (error) {
1499
+ const currentName = configuredAgentName();
1500
+ return LocalExecutorStatusSchema.parse({
1501
+ kind: "interf-local-executor-status",
1502
+ version: 1,
1503
+ status: "unavailable",
1504
+ checked_at: checkedAt,
1505
+ executor: null,
1506
+ available_executors: detectedExecutorOptions(currentName),
1507
+ message: error instanceof Error ? error.message : String(error),
1508
+ });
1509
+ }
1510
+ }
1511
+ selectExecutor(requestValue) {
1512
+ const request = LocalExecutorSelectRequestSchema.parse(requestValue);
1513
+ const selected = detectAgents()
1514
+ .filter(supportsAutomatedRuns)
1515
+ .find((agent) => agent.name === request.agent);
1516
+ if (!selected) {
1517
+ throw new Error(`Local agent "${request.agent}" is not detected or cannot run Interf runs.`);
1518
+ }
1519
+ const current = loadUserConfig();
1520
+ saveUserConfig({
1521
+ agent: selected.name,
1522
+ agentCommand: selected.command,
1523
+ skillsInstalled: current?.skillsInstalled ?? false,
1524
+ initialized: current?.initialized ?? new Date().toISOString(),
1525
+ });
1526
+ return this.getExecutorStatus();
1527
+ }
1528
+ listActionProposals() {
1529
+ return newestActionProposalFirst(listJsonFiles(actionProposalsRoot(this.rootPath))
1530
+ .map(readActionProposalAt)
1531
+ .filter((proposal) => proposal !== null));
1532
+ }
1533
+ getActionProposal(proposalId) {
1534
+ return this.listActionProposals().find((proposal) => proposal.proposal_id === proposalId) ?? null;
1535
+ }
1536
+ async createActionProposal(requestValue) {
1537
+ const request = ActionProposalCreateRequestSchema.parse(requestValue);
1538
+ const proposal = await this.buildActionProposal(request);
1539
+ this.writeActionProposal(proposal);
1540
+ return proposal;
1541
+ }
1542
+ async decideActionProposal(proposalId, requestValue) {
1543
+ const decision = ActionProposalApprovalRequestSchema.parse(requestValue);
1544
+ const current = this.getActionProposal(proposalId);
1545
+ if (!current)
1546
+ return null;
1547
+ if (current.status !== "awaiting_approval") {
1548
+ throw new Error(`Action proposal "${proposalId}" is already ${current.status}.`);
1549
+ }
1550
+ const now = new Date().toISOString();
1551
+ const decided = ActionProposalResourceSchema.parse({
1552
+ ...current,
1553
+ status: decision.approved ? "approved" : "denied",
1554
+ updated_at: now,
1555
+ approval: {
1556
+ approved: decision.approved,
1557
+ decided_at: now,
1558
+ ...(decision.note ? { note: decision.note } : {}),
1559
+ },
1560
+ });
1561
+ this.writeActionProposal(decided);
1562
+ if (!decision.approved)
1563
+ return decided;
1564
+ try {
1565
+ const submission = await this.submitActionProposal(decided);
1566
+ const submitted = ActionProposalResourceSchema.parse({
1567
+ ...decided,
1568
+ status: "submitted",
1569
+ updated_at: new Date().toISOString(),
1570
+ submitted_run_id: submission.runId,
1571
+ submitted_run_type: submission.runType,
1572
+ });
1573
+ this.writeActionProposal(submitted);
1574
+ return submitted;
1575
+ }
1576
+ catch (error) {
1577
+ const failed = ActionProposalResourceSchema.parse({
1578
+ ...decided,
1579
+ status: "failed",
1580
+ updated_at: new Date().toISOString(),
1581
+ error: error instanceof Error ? error.message : String(error),
1582
+ });
1583
+ this.writeActionProposal(failed);
1584
+ return failed;
1585
+ }
1586
+ }
1587
+ listRunObservability() {
1588
+ return [
1589
+ ...this.listCompileRuns().map((resource) => compileRunToObservability(resource.run)),
1590
+ ...this.listTestRuns().map(testRunToObservability),
1591
+ ...this.listJobs().map(jobRunToObservability),
1592
+ ].sort((left, right) => {
1593
+ const leftTime = timestampKey(left.started_at ?? left.created_at ?? left.finished_at);
1594
+ const rightTime = timestampKey(right.started_at ?? right.created_at ?? right.finished_at);
1595
+ return rightTime - leftTime;
1596
+ });
1597
+ }
1598
+ getRunObservability(runId) {
1599
+ return this.listRunObservability().find((run) => run.run_id === runId) ?? null;
1600
+ }
1601
+ createJobRun(requestValue) {
1602
+ const request = LocalJobRunCreateRequestSchema.parse(requestValue);
1603
+ const runId = createRunId("job");
1604
+ const now = new Date().toISOString();
1605
+ const run = LocalJobRunResourceSchema.parse({
1606
+ kind: "interf-local-job-run",
1607
+ version: 1,
1608
+ run_id: runId,
1609
+ job_type: request.job_type,
1610
+ status: "running",
1611
+ title: request.title,
1612
+ ...(request.preparation !== undefined ? { preparation: request.preparation } : {}),
1613
+ ...(request.method !== undefined ? { method: request.method } : {}),
1614
+ ...(request.source_path !== undefined ? { source_path: request.source_path } : {}),
1615
+ ...(request.output_path !== undefined ? { output_path: request.output_path } : {}),
1616
+ ...(request.agent !== undefined ? { agent: request.agent } : {}),
1617
+ created_at: now,
1618
+ started_at: now,
1619
+ steps: request.steps.map((step) => ({
1620
+ ...step,
1621
+ status: "queued",
1622
+ })),
1623
+ events: [
1624
+ {
1625
+ type: "job.started",
1626
+ event_id: createRunEventId("event"),
1627
+ run_id: runId,
1628
+ timestamp: now,
1629
+ message: request.title,
1630
+ },
1631
+ ],
1632
+ });
1633
+ this.writeJobRun(run);
1634
+ return run;
1635
+ }
1636
+ appendJobRunEvent(runId, requestValue) {
1637
+ const request = LocalJobEventAppendRequestSchema.parse(requestValue);
1638
+ const current = this.getJob(runId);
1639
+ if (!current)
1640
+ return null;
1641
+ const event = {
1642
+ type: request.type,
1643
+ event_id: createRunEventId("event"),
1644
+ run_id: runId,
1645
+ timestamp: new Date().toISOString(),
1646
+ ...(request.step_id ? { step_id: request.step_id } : {}),
1647
+ ...(request.message ? { message: request.message } : {}),
1648
+ ...(request.artifact ? { artifact: request.artifact } : {}),
1649
+ ...(request.input ? { input: request.input } : {}),
1650
+ ...(request.output ? { output: request.output } : {}),
1651
+ };
1652
+ const next = LocalJobRunResourceSchema.parse(applyEventToLocalJob(current, event));
1653
+ this.writeJobRun(next);
1654
+ return next;
1655
+ }
1656
+ async createReadinessCheckDraftRun(requestValue) {
1657
+ const request = ReadinessCheckDraftCreateRequestSchema.parse(requestValue);
1658
+ const job = this.createJobRun({
1659
+ job_type: "readiness-check-draft",
1660
+ title: `Draft readiness checks for ${request.preparation}`,
1661
+ preparation: request.preparation,
1662
+ source_path: request.source_folder_path,
1663
+ steps: [
1664
+ {
1665
+ id: "read-source",
1666
+ label: "Read source files",
1667
+ input: {
1668
+ preparation: request.preparation,
1669
+ source_folder_path: request.source_folder_path,
1670
+ },
1671
+ },
1672
+ {
1673
+ id: "agent-draft",
1674
+ label: "Draft readiness checks",
1675
+ input: {
1676
+ about: request.about ?? null,
1677
+ target_count: request.target_count,
1678
+ },
1679
+ },
1680
+ {
1681
+ id: "normalize-checks",
1682
+ label: "Normalize saved readiness checks",
1683
+ },
1684
+ ],
1685
+ });
1686
+ this.appendJobRunEvent(job.run_id, {
1687
+ type: "step.started",
1688
+ step_id: "read-source",
1689
+ message: "Reading source files for readiness-check evidence.",
1690
+ input: {
1691
+ preparation: request.preparation,
1692
+ source_folder_path: request.source_folder_path,
1693
+ },
1694
+ });
1695
+ this.appendJobRunEvent(job.run_id, {
1696
+ type: "step.completed",
1697
+ step_id: "read-source",
1698
+ message: "Source folder is ready for readiness-check drafting.",
1699
+ output: {
1700
+ preparation: request.preparation,
1701
+ source_folder_path: request.source_folder_path,
1702
+ },
1703
+ });
1704
+ this.appendJobRunEvent(job.run_id, {
1705
+ type: "step.started",
1706
+ step_id: "agent-draft",
1707
+ message: "Drafting saved readiness checks from the source files.",
1708
+ input: {
1709
+ about: request.about ?? null,
1710
+ target_count: request.target_count,
1711
+ },
1712
+ });
1713
+ void this.runReadinessCheckDraftInBackground(request, job.run_id);
1714
+ return this.getJob(job.run_id) ?? job;
1715
+ }
1716
+ async createDatasetSetupRun(requestValue) {
1717
+ const request = DatasetSetupCreateRequestSchema.parse(requestValue);
1718
+ const datasetConfig = request.preparation;
1719
+ const methodId = methodIdForSourcePreparationConfig(datasetConfig) ?? "interf-default";
1720
+ const sourceFolderPath = resolveSourceDatasetPath(this.rootPath, datasetConfig);
1721
+ const outputPath = portableContextPath(this.rootPath, datasetConfig.name);
1722
+ const job = this.createJobRun({
1723
+ job_type: "preparation-setup",
1724
+ title: `Create Preparation ${datasetConfig.name}`,
1725
+ preparation: datasetConfig.name,
1726
+ method: methodId,
1727
+ source_path: sourceFolderPath,
1728
+ output_path: outputPath,
1729
+ agent: this.getExecutorStatus().executor,
1730
+ steps: [
1731
+ {
1732
+ id: "validate-source",
1733
+ label: "Validate source folder",
1734
+ input: {
1735
+ preparation: datasetConfig.name,
1736
+ path: datasetConfig.path,
1737
+ },
1738
+ },
1739
+ {
1740
+ id: "write-config",
1741
+ label: "Save Preparation config",
1742
+ input: {
1743
+ config_path: join(this.rootPath, "interf", "interf.json"),
1744
+ },
1745
+ },
1746
+ {
1747
+ id: "prepare-scaffold",
1748
+ label: "Prepare portable context",
1749
+ input: {
1750
+ output_path: outputPath,
1751
+ method: methodId,
1752
+ },
1753
+ },
1754
+ ],
1755
+ });
1756
+ let activeStep = "validate-source";
1757
+ try {
1758
+ this.appendJobRunEvent(job.run_id, {
1759
+ type: "step.started",
1760
+ step_id: "validate-source",
1761
+ message: "Validating source folder.",
1762
+ input: {
1763
+ path: datasetConfig.path,
1764
+ source_folder_path: sourceFolderPath,
1765
+ },
1766
+ });
1767
+ if (!existsSync(sourceFolderPath) || !statSync(sourceFolderPath).isDirectory()) {
1768
+ throw new Error(`Source folder "${datasetConfig.path}" is not available.`);
1769
+ }
1770
+ this.appendJobRunEvent(job.run_id, {
1771
+ type: "step.completed",
1772
+ step_id: "validate-source",
1773
+ message: "Source folder is available.",
1774
+ output: {
1775
+ source_folder_path: sourceFolderPath,
1776
+ },
1777
+ });
1778
+ activeStep = "write-config";
1779
+ this.appendJobRunEvent(job.run_id, {
1780
+ type: "step.started",
1781
+ step_id: "write-config",
1782
+ message: "Saving Preparation in the control plane config.",
1783
+ input: {
1784
+ preparation: datasetConfig.name,
1785
+ path: datasetConfig.path,
1786
+ },
1787
+ });
1788
+ upsertSourceDatasetConfig(this.rootPath, datasetConfig);
1789
+ this.appendJobRunEvent(job.run_id, {
1790
+ type: "step.completed",
1791
+ step_id: "write-config",
1792
+ message: "Preparation config saved.",
1793
+ output: {
1794
+ config_path: join(this.rootPath, "interf", "interf.json"),
1795
+ preparation: datasetConfig.name,
1796
+ },
1797
+ });
1798
+ activeStep = "prepare-scaffold";
1799
+ this.appendJobRunEvent(job.run_id, {
1800
+ type: "step.started",
1801
+ step_id: "prepare-scaffold",
1802
+ message: "Preparing portable-context scaffold.",
1803
+ input: {
1804
+ output_path: outputPath,
1805
+ method: methodId,
1806
+ },
1807
+ });
1808
+ const compiledPath = ensurePortableContextScaffold(this.rootPath, datasetConfig.name, methodId);
1809
+ syncCompiledInterfConfigFromSourceDatasetConfig(compiledPath, datasetConfig);
1810
+ this.appendJobRunEvent(job.run_id, {
1811
+ type: "step.completed",
1812
+ step_id: "prepare-scaffold",
1813
+ message: "Portable-context scaffold is ready.",
1814
+ output: {
1815
+ portable_context_path: compiledPath,
1816
+ },
1817
+ });
1818
+ this.setJobRunResult(job.run_id, {
1819
+ preparation: datasetConfig,
1820
+ source_folder_path: sourceFolderPath,
1821
+ portable_context_path: compiledPath,
1822
+ });
1823
+ this.appendJobRunEvent(job.run_id, {
1824
+ type: "job.completed",
1825
+ message: `Preparation ${datasetConfig.name} is ready for Interf runs.`,
1826
+ });
1827
+ }
1828
+ catch (error) {
1829
+ const message = error instanceof Error ? error.message : String(error);
1830
+ this.appendJobRunEvent(job.run_id, {
1831
+ type: "step.failed",
1832
+ step_id: activeStep,
1833
+ message,
1834
+ output: {
1835
+ error: message,
1836
+ },
1837
+ });
1838
+ this.appendJobRunEvent(job.run_id, {
1839
+ type: "job.failed",
1840
+ message,
1841
+ });
1842
+ }
1843
+ return this.getJob(job.run_id) ?? job;
1844
+ }
1845
+ async createWorkflowAuthoringRun(requestValue) {
1846
+ const request = WorkflowAuthoringCreateRequestSchema.parse(requestValue);
1847
+ const job = this.createJobRun({
1848
+ job_type: "method-authoring",
1849
+ title: `Draft Method ${request.method_id}`,
1850
+ preparation: request.preparation ?? null,
1851
+ method: request.method_id,
1852
+ source_path: request.source_folder_path,
1853
+ output_path: join(this.rootPath, "interf", "methods", request.method_id),
1854
+ steps: [
1855
+ {
1856
+ id: "inspect-source",
1857
+ label: "Inspect source files",
1858
+ input: {
1859
+ preparation: request.preparation ?? null,
1860
+ source_folder_path: request.source_folder_path,
1861
+ checks: request.checks.length,
1862
+ },
1863
+ },
1864
+ {
1865
+ id: "draft-package",
1866
+ label: "Draft Method package",
1867
+ input: {
1868
+ method_id: request.method_id,
1869
+ label: request.label,
1870
+ task_prompt: request.task_prompt,
1871
+ },
1872
+ },
1873
+ {
1874
+ id: "validate-package",
1875
+ label: "Validate Method package",
1876
+ input: {
1877
+ method_id: request.method_id,
1878
+ },
1879
+ },
1880
+ ],
1881
+ });
1882
+ this.appendJobRunEvent(job.run_id, {
1883
+ type: "step.started",
1884
+ step_id: "inspect-source",
1885
+ message: "Inspecting source files for Method drafting.",
1886
+ input: {
1887
+ preparation: request.preparation ?? null,
1888
+ source_folder_path: request.source_folder_path,
1889
+ checks: request.checks.length,
1890
+ },
1891
+ });
1892
+ this.appendJobRunEvent(job.run_id, {
1893
+ type: "step.completed",
1894
+ step_id: "inspect-source",
1895
+ message: "Source context is ready.",
1896
+ output: {
1897
+ source_folder_path: request.source_folder_path,
1898
+ checks: request.checks.length,
1899
+ },
1900
+ });
1901
+ this.appendJobRunEvent(job.run_id, {
1902
+ type: "step.started",
1903
+ step_id: "draft-package",
1904
+ message: "Drafting Method package.",
1905
+ input: {
1906
+ method_id: request.method_id,
1907
+ label: request.label,
1908
+ task_prompt: request.task_prompt,
1909
+ },
1910
+ });
1911
+ void this.runWorkflowAuthoringInBackground(request, job.run_id);
1912
+ return this.getJob(job.run_id) ?? job;
1913
+ }
1914
+ listPortableContexts() {
1915
+ return this.listDatasets().map((dataset) => this.getPortableContext(dataset.dataset.name))
1916
+ .filter((context) => context !== null);
1917
+ }
1918
+ getPortableContext(datasetName) {
1919
+ const dataset = findSourceDatasetConfig(loadSourceFolderConfig(this.rootPath), datasetName);
1920
+ if (!dataset)
1921
+ return null;
1922
+ const path = portableContextPath(this.rootPath, dataset.name);
1923
+ const config = readInterfConfig(path);
1924
+ const compileRuns = this.listCompileRunsForDataset(dataset.name);
1925
+ const testRuns = this.listTestRunsForDataset(dataset.name);
1926
+ const readiness = this.computeDatasetReadiness(dataset);
1927
+ const method = config?.method ?? config?.workflow ?? methodIdForSourcePreparationConfig(dataset);
1928
+ return PortableContextResourceSchema.parse({
1929
+ preparation: dataset.name,
1930
+ path,
1931
+ exists: existsSync(path),
1932
+ readiness,
1933
+ method,
1934
+ latest_compile_run_id: compileRuns[0]?.run_id ?? null,
1935
+ latest_test_run_id: testRuns[0]?.run_id ?? null,
1936
+ artifacts: uniqueArtifacts(compileRuns[0]?.stages.flatMap((stage) => stage.artifacts) ?? []),
1937
+ });
1938
+ }
1939
+ listCompileRuns() {
1940
+ return newestCompileFirst(this.listDatasets().flatMap((dataset) => this.listCompileRunsForDataset(dataset.dataset.name))).map((run) => CompileRunResourceSchema.parse({ run }));
1941
+ }
1942
+ listCompileRunsForDataset(datasetName) {
1943
+ const compiledPath = portableContextPath(this.rootPath, datasetName);
1944
+ return newestCompileFirst(listJsonFiles(compileRunsRoot(compiledPath))
1945
+ .map(readCompileRunAt)
1946
+ .filter((run) => run !== null));
1947
+ }
1948
+ getCompileRun(runId) {
1949
+ for (const resource of this.listCompileRuns()) {
1950
+ if (resource.run.run_id === runId)
1951
+ return resource;
1952
+ }
1953
+ return null;
1954
+ }
1955
+ getCompileRunEvents(runId) {
1956
+ return this.getCompileRun(runId)?.run.events ?? null;
1957
+ }
1958
+ getCompileRunProof(runId) {
1959
+ const run = this.getCompileRun(runId)?.run;
1960
+ if (!run)
1961
+ return null;
1962
+ return run.stages
1963
+ .map((stage) => stage.latest_proof ?? null)
1964
+ .filter((proof) => proof !== null);
1965
+ }
1966
+ getCompileRunArtifacts(runId) {
1967
+ const run = this.getCompileRun(runId)?.run;
1968
+ if (!run)
1969
+ return null;
1970
+ return uniqueArtifacts(run.stages.flatMap((stage) => stage.artifacts));
1971
+ }
1972
+ async createCompileRun(requestValue) {
1973
+ const request = CompileRunCreateRequestSchema.parse(requestValue);
1974
+ const datasetConfig = this.resolveDatasetConfig(request.preparation, {
1975
+ method: request.method,
1976
+ max_attempts: request.max_attempts,
1977
+ max_loops: request.max_loops,
1978
+ });
1979
+ const compiledPath = this.ensureCompiledForRun(datasetConfig);
1980
+ const runId = createRunId("compile");
1981
+ const now = new Date().toISOString();
1982
+ const workflow = getCompiledWorkflow(methodIdForSourcePreparationConfig(datasetConfig) ?? "interf", {
1983
+ sourcePath: this.rootPath,
1984
+ });
1985
+ const stageTotal = workflow.stages.length;
1986
+ const run = CompileRunSchema.parse({
1987
+ kind: "interf-compile-run",
1988
+ version: 1,
1989
+ run_id: runId,
1990
+ status: "running",
1991
+ preparation: datasetConfig.name,
1992
+ method: workflow.id,
1993
+ backend: "native",
1994
+ source_path: resolveSourceDatasetPath(this.rootPath, datasetConfig),
1995
+ portable_context_path: compiledPath,
1996
+ created_at: now,
1997
+ started_at: now,
1998
+ stages: workflow.stages.map((stage, index) => ({
1999
+ run_id: runId,
2000
+ stage_id: stage.id,
2001
+ stage_label: stage.label,
2002
+ stage_index: index,
2003
+ stage_total: stageTotal,
2004
+ status: "queued",
2005
+ contract: {
2006
+ stage_label: stage.label,
2007
+ stage_index: index,
2008
+ stage_total: stageTotal,
2009
+ reads: stage.reads,
2010
+ writes: stage.writes,
2011
+ ...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
2012
+ },
2013
+ artifacts: [],
2014
+ })),
2015
+ events: [],
2016
+ });
2017
+ this.writeCompileRun(compiledPath, run);
2018
+ await this.recordCompileRunEvent(compiledPath, runId, {
2019
+ type: "run.started",
2020
+ event_id: createRunEventId("event"),
2021
+ run_id: runId,
2022
+ timestamp: now,
2023
+ preparation: datasetConfig.name,
2024
+ method: workflow.id,
2025
+ portable_context_path: compiledPath,
2026
+ backend: "native",
2027
+ });
2028
+ const sink = {
2029
+ emit: (event) => this.recordCompileRunEvent(compiledPath, runId, event),
2030
+ };
2031
+ void this.runCompileInBackground(request, {
2032
+ runId,
2033
+ sourcePath: this.rootPath,
2034
+ compiledPath,
2035
+ datasetConfig,
2036
+ events: sink,
2037
+ });
2038
+ const saved = this.readCompileRun(compiledPath, runId) ?? run;
2039
+ return CompileRunResourceSchema.parse({ run: saved });
2040
+ }
2041
+ listTestRuns() {
2042
+ return newestFirst(this.listDatasets().flatMap((dataset) => this.listTestRunsForDataset(dataset.dataset.name)));
2043
+ }
2044
+ listTestRunsForDataset(datasetName) {
2045
+ const compiledPath = portableContextPath(this.rootPath, datasetName);
2046
+ return newestFirst(listJsonFiles(testRunsRoot(compiledPath))
2047
+ .map(readTestRunAt)
2048
+ .filter((run) => run !== null));
2049
+ }
2050
+ getTestRun(runId) {
2051
+ return this.listTestRuns().find((run) => run.run_id === runId) ?? null;
2052
+ }
2053
+ async createTestRun(requestValue) {
2054
+ const request = TestRunCreateRequestSchema.parse(requestValue);
2055
+ const datasetConfig = this.resolveDatasetConfig(request.preparation);
2056
+ const compiledPath = portableContextPath(this.rootPath, datasetConfig.name);
2057
+ const compiledTarget = createCompiledTestTarget(compiledPath, datasetConfig.name, methodIdForSourcePreparationConfig(datasetConfig) ?? "interf");
2058
+ const runId = createRunId("test");
2059
+ const now = new Date().toISOString();
2060
+ const initial = TestRunResourceSchema.parse({
2061
+ run_id: runId,
2062
+ status: "running",
2063
+ preparation: datasetConfig.name,
2064
+ mode: request.mode,
2065
+ source_path: this.rootPath,
2066
+ portable_context_path: compiledTarget.eligible ? compiledPath : null,
2067
+ started_at: now,
2068
+ comparison: null,
2069
+ events: [],
2070
+ });
2071
+ this.writeTestRun(compiledPath, initial);
2072
+ void this.runTestInBackground(request, {
2073
+ runId,
2074
+ sourcePath: this.rootPath,
2075
+ compiledPath,
2076
+ datasetConfig,
2077
+ }, initial);
2078
+ return initial;
2079
+ }
2080
+ async runCompileInBackground(request, context) {
2081
+ try {
2082
+ if (!this.handlers.createCompileRun) {
2083
+ throw new Error("No compile-run handler is configured for this local service.");
2084
+ }
2085
+ const result = LocalRunHandlerResultSchema.parse(await this.handlers.createCompileRun(request, context));
2086
+ this.refreshCompileRunFromRuntime(context.compiledPath, context.runId);
2087
+ await this.emitRuntimeDerivedEvents(context.compiledPath, context.runId);
2088
+ if (!result.ok) {
2089
+ await this.recordCompileRunEvent(context.compiledPath, context.runId, {
2090
+ type: "run.failed",
2091
+ event_id: createRunEventId("event"),
2092
+ run_id: context.runId,
2093
+ timestamp: createRunEventTimestamp(),
2094
+ error: result.error ?? "Compile run failed.",
2095
+ });
2096
+ }
2097
+ else {
2098
+ await this.recordCompileRunEvent(context.compiledPath, context.runId, {
2099
+ type: "run.completed",
2100
+ event_id: createRunEventId("event"),
2101
+ run_id: context.runId,
2102
+ timestamp: createRunEventTimestamp(),
2103
+ summary: "Portable context ready.",
2104
+ });
2105
+ }
2106
+ }
2107
+ catch (error) {
2108
+ await this.recordCompileRunEvent(context.compiledPath, context.runId, {
2109
+ type: "run.failed",
2110
+ event_id: createRunEventId("event"),
2111
+ run_id: context.runId,
2112
+ timestamp: createRunEventTimestamp(),
2113
+ error: error instanceof Error ? error.message : String(error),
2114
+ });
2115
+ }
2116
+ }
2117
+ async runTestInBackground(request, context, initial) {
2118
+ try {
2119
+ if (!this.handlers.createTestRun) {
2120
+ throw new Error("No test-run handler is configured for this local service.");
2121
+ }
2122
+ const result = LocalRunHandlerResultSchema.parse(await this.handlers.createTestRun(request, context));
2123
+ const comparison = this.readLatestComparison(context.datasetConfig.name);
2124
+ const resultEvent = comparison
2125
+ ? this.checksEvaluatedEvent(context.runId, comparison)
2126
+ : null;
2127
+ const nextWithoutReadiness = TestRunResourceSchema.parse({
2128
+ ...initial,
2129
+ status: result.ok ? "succeeded" : "failed",
2130
+ finished_at: new Date().toISOString(),
2131
+ comparison,
2132
+ events: resultEvent ? [resultEvent] : [],
2133
+ ...(!result.ok ? { error: result.error ?? "Readiness check failed." } : {}),
2134
+ });
2135
+ this.writeTestRun(context.compiledPath, nextWithoutReadiness);
2136
+ const readiness = this.computeDatasetReadiness(context.datasetConfig);
2137
+ const next = TestRunResourceSchema.parse({
2138
+ ...nextWithoutReadiness,
2139
+ readiness,
2140
+ events: [
2141
+ ...nextWithoutReadiness.events,
2142
+ this.readinessUpdatedEvent(context.runId, context.datasetConfig.name, readiness),
2143
+ ],
2144
+ });
2145
+ this.writeTestRun(context.compiledPath, next);
2146
+ }
2147
+ catch (error) {
2148
+ const failedWithoutReadiness = TestRunResourceSchema.parse({
2149
+ ...initial,
2150
+ status: "failed",
2151
+ finished_at: new Date().toISOString(),
2152
+ error: error instanceof Error ? error.message : String(error),
2153
+ });
2154
+ this.writeTestRun(context.compiledPath, failedWithoutReadiness);
2155
+ const readiness = this.computeDatasetReadiness(context.datasetConfig);
2156
+ const next = TestRunResourceSchema.parse({
2157
+ ...failedWithoutReadiness,
2158
+ readiness,
2159
+ events: [this.readinessUpdatedEvent(context.runId, context.datasetConfig.name, readiness)],
2160
+ });
2161
+ this.writeTestRun(context.compiledPath, next);
2162
+ }
2163
+ }
2164
+ async runReadinessCheckDraftInBackground(request, runId) {
2165
+ try {
2166
+ if (!this.handlers.createReadinessCheckDraft) {
2167
+ throw new Error("No readiness-check-draft handler is configured for this local service.");
2168
+ }
2169
+ const result = ReadinessCheckDraftResultSchema.parse(await this.handlers.createReadinessCheckDraft(request, this.jobRunContext(runId)));
2170
+ this.appendJobRunEvent(runId, {
2171
+ type: "step.completed",
2172
+ step_id: "agent-draft",
2173
+ message: `Drafted ${result.checks.length} readiness checks.`,
2174
+ output: {
2175
+ checks: result.checks,
2176
+ },
2177
+ });
2178
+ this.appendJobRunEvent(runId, {
2179
+ type: "step.started",
2180
+ step_id: "normalize-checks",
2181
+ message: "Normalizing readiness-check draft into saved check records.",
2182
+ input: {
2183
+ checks: result.checks.length,
2184
+ },
2185
+ });
2186
+ this.appendJobRunEvent(runId, {
2187
+ type: "step.completed",
2188
+ step_id: "normalize-checks",
2189
+ message: `${result.checks.length} readiness checks ready for review.`,
2190
+ output: {
2191
+ checks: result.checks.length,
2192
+ },
2193
+ });
2194
+ this.setJobRunResult(runId, result);
2195
+ this.appendJobRunEvent(runId, {
2196
+ type: "job.completed",
2197
+ message: `Drafted ${result.checks.length} readiness checks.`,
2198
+ });
2199
+ }
2200
+ catch (error) {
2201
+ const message = error instanceof Error ? error.message : String(error);
2202
+ this.appendJobRunEvent(runId, {
2203
+ type: "step.failed",
2204
+ step_id: "agent-draft",
2205
+ message,
2206
+ output: {
2207
+ error: message,
2208
+ },
2209
+ });
2210
+ this.appendJobRunEvent(runId, {
2211
+ type: "job.failed",
2212
+ message,
2213
+ });
2214
+ }
2215
+ }
2216
+ async runWorkflowAuthoringInBackground(request, runId) {
2217
+ try {
2218
+ if (!this.handlers.createWorkflowAuthoringRun) {
2219
+ throw new Error("No Method-authoring handler is configured for this local service.");
2220
+ }
2221
+ const result = WorkflowAuthoringResultSchema.parse(await this.handlers.createWorkflowAuthoringRun(request, this.jobRunContext(runId)));
2222
+ this.setJobRunResult(runId, result);
2223
+ this.appendJobRunEvent(runId, {
2224
+ type: result.status === "executor-failed" ? "step.failed" : "step.completed",
2225
+ step_id: "draft-package",
2226
+ message: result.summary,
2227
+ output: {
2228
+ status: result.status,
2229
+ changed: result.changed,
2230
+ method_path: result.method_path,
2231
+ shell_path: result.shell_path,
2232
+ },
2233
+ });
2234
+ this.appendJobRunEvent(runId, {
2235
+ type: "step.started",
2236
+ step_id: "validate-package",
2237
+ message: "Validating Method package structure and stage contract.",
2238
+ input: {
2239
+ method_path: result.method_path,
2240
+ },
2241
+ });
2242
+ if (result.status === "updated" || result.status === "no-change") {
2243
+ this.appendJobRunEvent(runId, {
2244
+ type: "step.completed",
2245
+ step_id: "validate-package",
2246
+ message: result.summary,
2247
+ output: {
2248
+ status: result.status,
2249
+ validation: result.validation ?? null,
2250
+ },
2251
+ });
2252
+ this.appendJobRunEvent(runId, {
2253
+ type: "job.completed",
2254
+ message: result.summary,
2255
+ });
2256
+ }
2257
+ else {
2258
+ this.appendJobRunEvent(runId, {
2259
+ type: "step.failed",
2260
+ step_id: "validate-package",
2261
+ message: result.summary,
2262
+ output: {
2263
+ status: result.status,
2264
+ validation: result.validation ?? null,
2265
+ },
2266
+ });
2267
+ this.appendJobRunEvent(runId, {
2268
+ type: "job.failed",
2269
+ message: result.summary,
2270
+ });
2271
+ }
2272
+ }
2273
+ catch (error) {
2274
+ const message = error instanceof Error ? error.message : String(error);
2275
+ this.appendJobRunEvent(runId, {
2276
+ type: "step.failed",
2277
+ step_id: "draft-package",
2278
+ message,
2279
+ output: {
2280
+ error: message,
2281
+ },
2282
+ });
2283
+ this.appendJobRunEvent(runId, {
2284
+ type: "job.failed",
2285
+ message,
2286
+ });
2287
+ }
2288
+ }
2289
+ jobRunContext(runId) {
2290
+ return {
2291
+ runId,
2292
+ sourcePath: this.rootPath,
2293
+ emit: (event) => {
2294
+ this.appendJobRunEvent(runId, event);
2295
+ },
2296
+ };
2297
+ }
2298
+ defaultDatasetName() {
2299
+ const dataset = listSourceDatasetConfigs(loadSourceFolderConfig(this.rootPath))[0];
2300
+ if (!dataset) {
2301
+ throw new Error("No Preparation is saved in this control plane folder.");
2302
+ }
2303
+ return dataset.name;
2304
+ }
2305
+ async planActionProposal(request) {
2306
+ if (!this.handlers.planActionProposal) {
2307
+ return ActionProposalPlanSchema.parse({
2308
+ action_type: "clarification",
2309
+ ...(request.preparation ? { preparation: request.preparation } : {}),
2310
+ assistant_message: "No local action planner is configured for this Interf Workspace.",
2311
+ });
2312
+ }
2313
+ const datasets = listSourceDatasetConfigs(loadSourceFolderConfig(this.rootPath));
2314
+ let rawPlan;
2315
+ try {
2316
+ rawPlan = await this.handlers.planActionProposal(request, {
2317
+ sourcePath: this.rootPath,
2318
+ datasets,
2319
+ preparationHealth: datasets.map((dataset) => {
2320
+ const readinessChecks = dataset.checks?.length ?? 0;
2321
+ const portableContextReady = hasCompiledTestTarget(this.rootPath, dataset);
2322
+ return {
2323
+ name: dataset.name,
2324
+ readiness_checks: readinessChecks,
2325
+ portable_context_ready: portableContextReady,
2326
+ can_check_readiness: readinessChecks > 0 && portableContextReady,
2327
+ recommended_next_actions: portableContextReady
2328
+ ? readinessChecks > 0
2329
+ ? ["check readiness", "improve", "prepare again"]
2330
+ : ["draft readiness checks", "prepare again"]
2331
+ : ["prepare"],
2332
+ };
2333
+ }),
2334
+ sourceFolders: listSourceFolderChoices(this.rootPath),
2335
+ recentProposals: this.listActionProposals().slice(0, 5),
2336
+ });
2337
+ }
2338
+ catch {
2339
+ return ActionProposalPlanSchema.parse({
2340
+ action_type: "clarification",
2341
+ ...(request.preparation ? { preparation: request.preparation } : {}),
2342
+ assistant_message: ACTION_PLANNER_CLARIFICATION_MESSAGE,
2343
+ });
2344
+ }
2345
+ const parsed = ActionProposalPlanSchema.safeParse(rawPlan);
2346
+ if (parsed.success)
2347
+ return parsed.data;
2348
+ return ActionProposalPlanSchema.parse({
2349
+ action_type: "clarification",
2350
+ ...(request.preparation ? { preparation: request.preparation } : {}),
2351
+ assistant_message: ACTION_PLANNER_CLARIFICATION_MESSAGE,
2352
+ });
2353
+ }
2354
+ buildDatasetSetupRequest(request, plan, values) {
2355
+ const sourceFolderChoices = listSourceFolderChoices(this.rootPath);
2356
+ const selectedPath = datasetSetupPathValue(values) ??
2357
+ (sourceFolderChoices.length === 1 ? sourceFolderChoices[0].value : null);
2358
+ if (!selectedPath) {
2359
+ return {
2360
+ error: "Tell Interf which source folder to use for the Preparation.",
2361
+ };
2362
+ }
2363
+ let normalizedPath;
2364
+ try {
2365
+ normalizedPath = normalizeSourceDatasetPathForConfig(this.rootPath, selectedPath);
2366
+ }
2367
+ catch (error) {
2368
+ return {
2369
+ error: error instanceof Error ? error.message : String(error),
2370
+ };
2371
+ }
2372
+ const explicitName = datasetSetupNameValue(values) ?? plan.preparation ?? request.preparation;
2373
+ const datasetName = explicitName
2374
+ ? slugFromText(explicitName)
2375
+ : defaultDatasetNameForPath(normalizedPath);
2376
+ const requestWorkflowId = stringValue(request.values, "method") ?? stringValue(request.values, "workflow");
2377
+ const valueWorkflowId = stringValue(values, "method") ?? stringValue(values, "workflow");
2378
+ const workflowId = requestWorkflowId ?? plan.method ?? valueWorkflowId ?? "interf-default";
2379
+ try {
2380
+ return {
2381
+ datasetName,
2382
+ workflowId,
2383
+ request: DatasetSetupCreateRequestSchema.parse({
2384
+ preparation: {
2385
+ name: datasetName,
2386
+ path: normalizedPath,
2387
+ about: stringValue(values, "about") ?? stringValue(values, "task_prompt") ?? request.message,
2388
+ method: workflowId,
2389
+ checks: [],
2390
+ },
2391
+ }),
2392
+ };
2393
+ }
2394
+ catch (error) {
2395
+ return {
2396
+ error: error instanceof Error ? error.message : String(error),
2397
+ };
2398
+ }
2399
+ }
2400
+ async buildActionProposal(request) {
2401
+ const plan = await this.planActionProposal(request);
2402
+ const actionType = plan.action_type;
2403
+ const now = new Date().toISOString();
2404
+ if (actionType === "clarification") {
2405
+ return ActionProposalResourceSchema.parse({
2406
+ kind: "interf-action-proposal",
2407
+ version: 1,
2408
+ proposal_id: createActionProposalId(),
2409
+ status: "needs_clarification",
2410
+ action_type: actionType,
2411
+ title: plan.title ?? "Clarify Interf action",
2412
+ summary: plan.summary ?? "No local run was proposed because the request did not resolve to a confirmed Interf action.",
2413
+ assistant_message: plan.assistant_message ?? ACTION_PLANNER_CLARIFICATION_MESSAGE,
2414
+ ...(plan.command_preview ? { command_preview: plan.command_preview } : {}),
2415
+ message: request.message,
2416
+ preparation: plan.preparation ?? request.preparation ?? null,
2417
+ method: plan.method ?? null,
2418
+ request: {
2419
+ message: request.message,
2420
+ ...(plan.values ? { values: plan.values } : {}),
2421
+ },
2422
+ created_at: now,
2423
+ updated_at: now,
2424
+ proposed_by_executor: this.getExecutorStatus().executor,
2425
+ approval: null,
2426
+ submitted_run_id: null,
2427
+ submitted_run_type: null,
2428
+ error: null,
2429
+ });
2430
+ }
2431
+ const proposalValues = {
2432
+ ...(plan.values ?? {}),
2433
+ ...(request.values ?? {}),
2434
+ };
2435
+ if (actionType === "preparation-setup") {
2436
+ const setup = this.buildDatasetSetupRequest(request, plan, proposalValues);
2437
+ if ("error" in setup) {
2438
+ return ActionProposalResourceSchema.parse({
2439
+ kind: "interf-action-proposal",
2440
+ version: 1,
2441
+ proposal_id: createActionProposalId(),
2442
+ status: "needs_clarification",
2443
+ action_type: "clarification",
2444
+ title: "Clarify Preparation setup",
2445
+ summary: "Interf needs a source folder before it can save a Preparation.",
2446
+ assistant_message: setup.error,
2447
+ message: request.message,
2448
+ preparation: plan.preparation ?? request.preparation ?? null,
2449
+ method: plan.method ?? null,
2450
+ request: {
2451
+ message: request.message,
2452
+ ...(proposalValues ? { values: proposalValues } : {}),
2453
+ },
2454
+ created_at: now,
2455
+ updated_at: now,
2456
+ proposed_by_executor: this.getExecutorStatus().executor,
2457
+ approval: null,
2458
+ submitted_run_id: null,
2459
+ submitted_run_type: null,
2460
+ error: null,
2461
+ });
2462
+ }
2463
+ const commandPreview = plan.command_preview ??
2464
+ actionCommandPreview(actionType, setup.datasetName, setup.workflowId, {
2465
+ ...proposalValues,
2466
+ path: setup.request.preparation.path,
2467
+ });
2468
+ return ActionProposalResourceSchema.parse({
2469
+ kind: "interf-action-proposal",
2470
+ version: 1,
2471
+ proposal_id: createActionProposalId(),
2472
+ status: "awaiting_approval",
2473
+ action_type: actionType,
2474
+ title: plan.title ?? `Create Preparation ${setup.datasetName}`,
2475
+ summary: plan.summary ?? "Save this source folder as an Interf Preparation.",
2476
+ assistant_message: plan.assistant_message ?? actionAssistantMessage(actionType, setup.datasetName, commandPreview),
2477
+ command_preview: commandPreview,
2478
+ message: request.message,
2479
+ preparation: setup.datasetName,
2480
+ method: setup.workflowId,
2481
+ request: setup.request,
2482
+ created_at: now,
2483
+ updated_at: now,
2484
+ proposed_by_executor: this.getExecutorStatus().executor,
2485
+ approval: null,
2486
+ submitted_run_id: null,
2487
+ submitted_run_type: null,
2488
+ error: null,
2489
+ });
2490
+ }
2491
+ const datasetConfig = this.resolveDatasetConfig(plan.preparation ?? request.preparation ?? this.defaultDatasetName());
2492
+ const datasetPath = resolveSourceDatasetPath(this.rootPath, datasetConfig);
2493
+ const requestedWorkflowId = stringValue(request.values, "method_id") ??
2494
+ stringValue(request.values, "method") ??
2495
+ stringValue(request.values, "workflow");
2496
+ const plannedWorkflowId = plan.method ??
2497
+ stringValue(plan.values, "method_id") ??
2498
+ stringValue(plan.values, "method") ??
2499
+ stringValue(plan.values, "workflow");
2500
+ const workflowId = actionType === "method-authoring"
2501
+ ? requestedWorkflowId ?? plannedWorkflowId ?? workflowIdForProposal(request.message, proposalValues)
2502
+ : requestedWorkflowId ?? plannedWorkflowId ?? methodIdForSourcePreparationConfig(datasetConfig);
2503
+ const clarifyResolvedAction = (options) => ActionProposalResourceSchema.parse({
2504
+ kind: "interf-action-proposal",
2505
+ version: 1,
2506
+ proposal_id: createActionProposalId(),
2507
+ status: "needs_clarification",
2508
+ action_type: "clarification",
2509
+ title: options.title,
2510
+ summary: options.summary,
2511
+ assistant_message: options.assistantMessage,
2512
+ message: request.message,
2513
+ preparation: datasetConfig.name,
2514
+ method: workflowId ?? null,
2515
+ request: {
2516
+ message: request.message,
2517
+ ...(proposalValues ? { values: proposalValues } : {}),
2518
+ },
2519
+ created_at: now,
2520
+ updated_at: now,
2521
+ proposed_by_executor: this.getExecutorStatus().executor,
2522
+ approval: null,
2523
+ submitted_run_id: null,
2524
+ submitted_run_type: null,
2525
+ error: null,
2526
+ });
2527
+ if (actionType === "test") {
2528
+ const requestedMode = testModeFromValues(proposalValues);
2529
+ const hasReadinessChecks = (datasetConfig.checks ?? []).length > 0;
2530
+ const portableContextReady = hasCompiledTestTarget(this.rootPath, datasetConfig);
2531
+ if (!hasReadinessChecks) {
2532
+ return clarifyResolvedAction({
2533
+ title: `Add readiness checks for ${datasetConfig.name}`,
2534
+ summary: "Readiness cannot be checked until this Preparation has saved checks.",
2535
+ assistantMessage: `Preparation "${datasetConfig.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.`,
2536
+ });
2537
+ }
2538
+ if (!portableContextReady && requestedMode !== "raw") {
2539
+ return clarifyResolvedAction({
2540
+ title: `Prepare ${datasetConfig.name} first`,
2541
+ summary: "Readiness checks need portable context unless you explicitly ask for a source-files-only baseline.",
2542
+ assistantMessage: `Preparation "${datasetConfig.name}" has no prepared portable context yet. Prepare it first; after that Interf can check readiness. If you specifically want a source-files-only baseline, ask for source files only.`,
2543
+ });
2544
+ }
2545
+ }
2546
+ const actionRequest = (() => {
2547
+ if (actionType === "compile") {
2548
+ return {
2549
+ preparation: datasetConfig.name,
2550
+ ...(workflowId ? { method: workflowId } : {}),
2551
+ };
2552
+ }
2553
+ if (actionType === "test") {
2554
+ const defaultMode = hasCompiledTestTarget(this.rootPath, datasetConfig) ? "both" : "raw";
2555
+ return {
2556
+ preparation: datasetConfig.name,
2557
+ mode: testModeValue(proposalValues, defaultMode),
2558
+ };
2559
+ }
2560
+ if (actionType === "readiness-check-draft") {
2561
+ return {
2562
+ preparation: datasetConfig.name,
2563
+ source_folder_path: datasetPath,
2564
+ about: stringValue(proposalValues, "about") ?? datasetConfig.about ?? request.message,
2565
+ target_count: Math.max(1, Math.min(8, Math.round(numberValue(proposalValues, "target_count") ?? 4))),
2566
+ };
2567
+ }
2568
+ return {
2569
+ preparation: datasetConfig.name,
2570
+ source_folder_path: datasetPath,
2571
+ method_id: workflowId,
2572
+ label: stringValue(proposalValues, "label") ?? `Custom ${datasetConfig.name}`,
2573
+ hint: stringValue(proposalValues, "hint") ?? request.message,
2574
+ task_prompt: actionValueMethodTaskPrompt(proposalValues) ?? stringValue(proposalValues, "task_prompt") ?? request.message,
2575
+ checks: datasetConfig.checks ?? [],
2576
+ };
2577
+ })();
2578
+ const title = (() => {
2579
+ if (plan.title)
2580
+ return plan.title;
2581
+ if (actionType === "compile")
2582
+ return `Prepare ${datasetConfig.name}`;
2583
+ if (actionType === "test")
2584
+ return `Check readiness for ${datasetConfig.name}`;
2585
+ if (actionType === "readiness-check-draft")
2586
+ return `Draft readiness checks for ${datasetConfig.name}`;
2587
+ return `Draft Method ${workflowId}`;
2588
+ })();
2589
+ const summary = (() => {
2590
+ if (plan.summary)
2591
+ return plan.summary;
2592
+ if (actionType === "compile")
2593
+ return "Build portable context agents can use.";
2594
+ if (actionType === "test")
2595
+ return "Run readiness checks against source files and portable context.";
2596
+ if (actionType === "readiness-check-draft")
2597
+ return "Ask the configured local executor to draft saved readiness checks.";
2598
+ return "Ask the configured local executor to create a reusable local Method.";
2599
+ })();
2600
+ const previewValues = actionType === "test"
2601
+ ? { mode: actionRequest.mode }
2602
+ : proposalValues;
2603
+ const commandPreview = plan.command_preview ?? actionCommandPreview(actionType, datasetConfig.name, workflowId, previewValues);
2604
+ return ActionProposalResourceSchema.parse({
2605
+ kind: "interf-action-proposal",
2606
+ version: 1,
2607
+ proposal_id: createActionProposalId(),
2608
+ status: "awaiting_approval",
2609
+ action_type: actionType,
2610
+ title,
2611
+ summary,
2612
+ assistant_message: plan.assistant_message ?? actionAssistantMessage(actionType, datasetConfig.name, commandPreview),
2613
+ command_preview: commandPreview,
2614
+ message: request.message,
2615
+ preparation: datasetConfig.name,
2616
+ method: workflowId,
2617
+ request: actionRequest,
2618
+ created_at: now,
2619
+ updated_at: now,
2620
+ proposed_by_executor: this.getExecutorStatus().executor,
2621
+ approval: null,
2622
+ submitted_run_id: null,
2623
+ submitted_run_type: null,
2624
+ error: null,
2625
+ });
2626
+ }
2627
+ async submitActionProposal(proposal) {
2628
+ if (proposal.action_type === "clarification") {
2629
+ throw new Error("Clarification proposals cannot be submitted.");
2630
+ }
2631
+ if (proposal.action_type === "compile") {
2632
+ const resource = await this.createCompileRun(proposal.request);
2633
+ return {
2634
+ runId: resource.run.run_id,
2635
+ runType: "compile-run",
2636
+ };
2637
+ }
2638
+ if (proposal.action_type === "test") {
2639
+ const resource = await this.createTestRun(proposal.request);
2640
+ return {
2641
+ runId: resource.run_id,
2642
+ runType: "test-run",
2643
+ };
2644
+ }
2645
+ if (proposal.action_type === "preparation-setup") {
2646
+ const job = await this.createDatasetSetupRun(proposal.request);
2647
+ return {
2648
+ runId: job.run_id,
2649
+ runType: "job-run",
2650
+ };
2651
+ }
2652
+ if (proposal.action_type === "readiness-check-draft") {
2653
+ const job = await this.createReadinessCheckDraftRun(proposal.request);
2654
+ return {
2655
+ runId: job.run_id,
2656
+ runType: "job-run",
2657
+ };
2658
+ }
2659
+ const job = await this.createWorkflowAuthoringRun(proposal.request);
2660
+ return {
2661
+ runId: job.run_id,
2662
+ runType: "job-run",
2663
+ };
2664
+ }
2665
+ resolveDatasetConfig(datasetName, overrides = {}) {
2666
+ const dataset = findSourceDatasetConfig(loadSourceFolderConfig(this.rootPath), datasetName);
2667
+ if (!dataset) {
2668
+ throw new Error(`Preparation "${datasetName}" is not saved in this control plane folder.`);
2669
+ }
2670
+ const method = overrides.method ?? overrides.workflow ?? methodIdForSourcePreparationConfig(dataset) ?? undefined;
2671
+ return {
2672
+ ...dataset,
2673
+ ...(method ? { method } : {}),
2674
+ ...(typeof overrides.max_attempts === "number" ? { max_attempts: overrides.max_attempts } : {}),
2675
+ ...(typeof overrides.max_loops === "number" ? { max_loops: overrides.max_loops } : {}),
2676
+ };
2677
+ }
2678
+ ensureCompiledForRun(datasetConfig) {
2679
+ const workflowId = methodIdForSourcePreparationConfig(datasetConfig) ?? "interf";
2680
+ const compiledPath = ensurePortableContextScaffold(this.rootPath, datasetConfig.name, workflowId);
2681
+ syncCompiledInterfConfigFromSourceDatasetConfig(compiledPath, datasetConfig);
2682
+ return compiledPath;
2683
+ }
2684
+ readCompileRun(compiledPath, runId) {
2685
+ return readCompileRunAt(compileRunPath(compiledPath, runId));
2686
+ }
2687
+ writeCompileRun(compiledPath, run) {
2688
+ mkdirSync(compileRunsRoot(compiledPath), { recursive: true });
2689
+ writeJsonFile(compileRunPath(compiledPath, run.run_id), CompileRunSchema.parse(run));
2690
+ }
2691
+ writeJobRun(run) {
2692
+ mkdirSync(localJobsRoot(this.rootPath), { recursive: true });
2693
+ writeJsonFile(localJobPath(this.rootPath, run.run_id), LocalJobRunResourceSchema.parse(run));
2694
+ }
2695
+ writeActionProposal(proposal) {
2696
+ mkdirSync(actionProposalsRoot(this.rootPath), { recursive: true });
2697
+ writeJsonFile(actionProposalPath(this.rootPath, proposal.proposal_id), ActionProposalResourceSchema.parse(proposal));
2698
+ }
2699
+ setJobRunResult(runId, result) {
2700
+ const current = this.getJob(runId);
2701
+ if (!current)
2702
+ return;
2703
+ const normalizedResult = result && typeof result === "object" && !Array.isArray(result)
2704
+ ? result
2705
+ : { value: result };
2706
+ this.writeJobRun(LocalJobRunResourceSchema.parse({
2707
+ ...current,
2708
+ result: normalizedResult,
2709
+ }));
2710
+ }
2711
+ async recordCompileRunEvent(compiledPath, runId, event) {
2712
+ const current = this.readCompileRun(compiledPath, runId);
2713
+ if (!current)
2714
+ return;
2715
+ this.writeCompileRun(compiledPath, applyEventToCompileRun(current, event));
2716
+ if (event.type === "stage.passed" || event.type === "stage.failed") {
2717
+ this.refreshCompileRunFromRuntime(compiledPath, runId);
2718
+ }
2719
+ }
2720
+ refreshCompileRunFromRuntime(compiledPath, runId) {
2721
+ const current = this.readCompileRun(compiledPath, runId);
2722
+ if (!current)
2723
+ return;
2724
+ const state = loadState(compiledPath);
2725
+ if (!state?.stages)
2726
+ return;
2727
+ const historyByStage = new Map();
2728
+ for (const run of readRuntimeRunHistory(compiledPath)) {
2729
+ if (run.target_name !== current.preparation)
2730
+ continue;
2731
+ const existing = historyByStage.get(run.stage);
2732
+ if (!existing || timestampKey(run.updated_at) >= timestampKey(existing.updated_at)) {
2733
+ historyByStage.set(run.stage, run);
2734
+ }
2735
+ }
2736
+ const next = {
2737
+ ...current,
2738
+ stages: current.stages.map((stage) => {
2739
+ const stageState = state.stages?.[stage.stage_id];
2740
+ if (!stageState)
2741
+ return stage;
2742
+ const runtimeRun = historyByStage.get(stage.stage_id);
2743
+ const artifacts = uniqueArtifacts([
2744
+ ...(stage.artifacts ?? []),
2745
+ ...stageArtifactRefs(stage.stage_id, stageState.artifacts),
2746
+ ]);
2747
+ const proof = stage.latest_proof ?? proofForStage({
2748
+ runId,
2749
+ stageId: stage.stage_id,
2750
+ summary: stageState.summary,
2751
+ stageState,
2752
+ artifacts,
2753
+ });
2754
+ return {
2755
+ ...stage,
2756
+ started_at: stage.started_at ?? stageState.started_at,
2757
+ finished_at: stage.finished_at ?? stageState.finished_at,
2758
+ summary: stage.summary ?? stageState.summary ?? null,
2759
+ executor: stage.executor ?? runtimeRun?.executor ?? null,
2760
+ artifacts,
2761
+ logs: stage.logs ?? logsForRuntimeRun(runtimeRun) ?? logsForStageRun(stageState),
2762
+ latest_proof: proof,
2763
+ };
2764
+ }),
2765
+ };
2766
+ next.latest_proof = [...next.stages].reverse().find((stage) => Boolean(stage.latest_proof))?.latest_proof ?? next.latest_proof;
2767
+ this.writeCompileRun(compiledPath, next);
2768
+ }
2769
+ async emitRuntimeDerivedEvents(compiledPath, runId) {
2770
+ const state = loadState(compiledPath);
2771
+ const run = this.readCompileRun(compiledPath, runId);
2772
+ if (!state?.stages || !run)
2773
+ return;
2774
+ for (const stage of run.stages) {
2775
+ const stageState = state.stages[stage.stage_id];
2776
+ if (!stageState)
2777
+ continue;
2778
+ const artifacts = stageArtifactRefs(stage.stage_id, stageState.artifacts);
2779
+ for (const artifact of artifacts) {
2780
+ await this.recordCompileRunEvent(compiledPath, runId, {
2781
+ type: "artifact.written",
2782
+ event_id: createRunEventId("event"),
2783
+ run_id: runId,
2784
+ timestamp: stageState.finished_at ?? createRunEventTimestamp(),
2785
+ stage_id: stage.stage_id,
2786
+ artifact,
2787
+ });
2788
+ }
2789
+ await this.recordCompileRunEvent(compiledPath, runId, {
2790
+ type: "proof.updated",
2791
+ event_id: createRunEventId("event"),
2792
+ run_id: runId,
2793
+ timestamp: stageState.finished_at ?? createRunEventTimestamp(),
2794
+ stage_id: stage.stage_id,
2795
+ proof: proofForStage({
2796
+ runId,
2797
+ stageId: stage.stage_id,
2798
+ summary: stageState.summary,
2799
+ stageState,
2800
+ artifacts,
2801
+ }),
2802
+ });
2803
+ }
2804
+ }
2805
+ readLatestComparison(datasetName) {
2806
+ const latestPath = datasetLatestTestStatePath(this.rootPath, datasetName);
2807
+ if (!existsSync(latestPath))
2808
+ return null;
2809
+ const parsed = TestRunComparisonSchema.safeParse(readJsonFile(latestPath));
2810
+ return parsed.success ? parsed.data : null;
2811
+ }
2812
+ checksEvaluatedEvent(runId, comparison) {
2813
+ const target = comparison.compiled ?? comparison.raw;
2814
+ return {
2815
+ type: "checks.evaluated",
2816
+ event_id: createRunEventId("event"),
2817
+ run_id: runId,
2818
+ timestamp: createRunEventTimestamp(),
2819
+ passed: target?.passed_cases ?? 0,
2820
+ total: target?.total_cases ?? 0,
2821
+ test_run_id: runId,
2822
+ };
2823
+ }
2824
+ readinessUpdatedEvent(runId, preparation, readiness) {
2825
+ return {
2826
+ type: "readiness.updated",
2827
+ event_id: createRunEventId("event"),
2828
+ run_id: runId,
2829
+ timestamp: createRunEventTimestamp(),
2830
+ preparation,
2831
+ readiness,
2832
+ };
2833
+ }
2834
+ writeTestRun(compiledPath, run) {
2835
+ mkdirSync(testRunsRoot(compiledPath), { recursive: true });
2836
+ writeJsonFile(testRunPath(compiledPath, run.run_id), TestRunResourceSchema.parse(run));
2837
+ }
2838
+ }
2839
+ export function createLocalServiceRuntime(options) {
2840
+ return new LocalServiceRuntime(options);
2841
+ }