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