@interf/compiler 0.9.5 → 0.16.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 +135 -91
- package/TRADEMARKS.md +2 -13
- package/agent-skills/interf-actions/SKILL.md +143 -38
- package/agent-skills/interf-actions/references/cli.md +134 -67
- package/builtin-methods/interf-default/README.md +3 -4
- package/builtin-methods/interf-default/compile/stages/shape/SKILL.md +2 -2
- package/builtin-methods/interf-default/compile/stages/summarize/SKILL.md +2 -1
- package/builtin-methods/interf-default/improve/SKILL.md +1 -1
- package/builtin-methods/interf-default/method.json +10 -4
- package/builtin-methods/interf-default/method.schema.json +0 -9
- package/builtin-methods/interf-default/use/query/SKILL.md +5 -5
- package/dist/cli/commands/agents.d.ts +2 -0
- package/dist/cli/commands/agents.js +213 -0
- package/dist/cli/commands/compile.d.ts +8 -25
- package/dist/cli/commands/compile.js +83 -359
- package/dist/cli/commands/doctor.js +2 -2
- package/dist/cli/commands/login.d.ts +7 -0
- package/dist/cli/commands/login.js +39 -0
- package/dist/cli/commands/logout.d.ts +2 -0
- package/dist/cli/commands/logout.js +16 -0
- package/dist/cli/commands/mcp.d.ts +42 -0
- package/dist/cli/commands/mcp.js +239 -0
- package/dist/cli/commands/method.d.ts +2 -0
- package/dist/cli/commands/method.js +113 -0
- package/dist/cli/commands/prep.d.ts +2 -0
- package/dist/cli/commands/prep.js +152 -0
- package/dist/cli/commands/reset.d.ts +8 -1
- package/dist/cli/commands/reset.js +47 -26
- package/dist/cli/commands/runs.d.ts +2 -0
- package/dist/cli/commands/runs.js +120 -0
- package/dist/cli/commands/status.d.ts +6 -1
- package/dist/cli/commands/status.js +68 -111
- package/dist/cli/commands/verify.d.ts +7 -1
- package/dist/cli/commands/verify.js +69 -85
- package/dist/cli/commands/web.d.ts +0 -9
- package/dist/cli/commands/web.js +201 -121
- package/dist/cli/commands/wizard.d.ts +9 -0
- package/dist/cli/commands/wizard.js +689 -0
- package/dist/cli/index.d.ts +10 -7
- package/dist/cli/index.js +19 -12
- package/dist/compiler-ui/404.html +1 -1
- package/dist/compiler-ui/__next.__PAGE__.txt +2 -2
- package/dist/compiler-ui/__next._full.txt +3 -3
- package/dist/compiler-ui/__next._head.txt +1 -1
- package/dist/compiler-ui/__next._index.txt +2 -2
- package/dist/compiler-ui/__next._tree.txt +2 -2
- package/dist/compiler-ui/_next/static/chunks/{177mvn4rse235.js → 0jipmpez3_ehh.js} +16 -16
- package/dist/compiler-ui/_next/static/chunks/{18a8f2jkv3z.c.css → 13awzu4tooflw.css} +1 -1
- package/dist/compiler-ui/_not-found/__next._full.txt +2 -2
- package/dist/compiler-ui/_not-found/__next._head.txt +1 -1
- package/dist/compiler-ui/_not-found/__next._index.txt +2 -2
- package/dist/compiler-ui/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/compiler-ui/_not-found/__next._not-found.txt +1 -1
- package/dist/compiler-ui/_not-found/__next._tree.txt +2 -2
- package/dist/compiler-ui/_not-found.html +1 -1
- package/dist/compiler-ui/_not-found.txt +2 -2
- package/dist/compiler-ui/index.html +1 -1
- package/dist/compiler-ui/index.txt +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/packages/contracts/index.d.ts +2 -1
- package/dist/packages/contracts/index.js +1 -0
- package/dist/packages/contracts/lib/preparation-paths.d.ts +117 -0
- package/dist/packages/contracts/lib/preparation-paths.js +177 -0
- package/dist/packages/contracts/lib/schema.d.ts +187 -13
- package/dist/packages/contracts/lib/schema.js +148 -3
- package/dist/packages/contracts/utils/filesystem.d.ts +9 -0
- package/dist/packages/contracts/utils/filesystem.js +142 -0
- package/dist/packages/{local-service → engine}/action-definitions.d.ts +14 -14
- package/dist/packages/{local-service → engine}/action-definitions.js +35 -29
- package/dist/packages/{local-service → engine}/action-planner.d.ts +1 -1
- package/dist/packages/{local-service → engine}/action-planner.js +3 -2
- package/dist/packages/{agents → engine/agents}/index.d.ts +3 -0
- package/dist/packages/{agents → engine/agents}/index.js +3 -0
- package/dist/packages/{agents → engine/agents}/lib/compiled-bootstrap.js +2 -2
- package/dist/packages/engine/agents/lib/detection.d.ts +13 -0
- package/dist/packages/{agents → engine/agents}/lib/detection.js +11 -0
- package/dist/packages/{agents → engine/agents}/lib/executors.d.ts +2 -2
- package/dist/packages/{agents → engine/agents}/lib/shells.d.ts +5 -5
- package/dist/packages/{agents → engine/agents}/lib/shells.js +117 -58
- package/dist/packages/{agents → engine/agents}/lib/user-config.d.ts +4 -2
- package/dist/packages/engine/agents/lib/user-config.js +24 -0
- package/dist/packages/engine/agents/registry.d.ts +91 -0
- package/dist/packages/engine/agents/registry.js +321 -0
- package/dist/packages/engine/agents/role-executors.d.ts +35 -0
- package/dist/packages/engine/agents/role-executors.js +88 -0
- package/dist/packages/engine/agents/role-router.d.ts +66 -0
- package/dist/packages/engine/agents/role-router.js +73 -0
- package/dist/packages/{local-service → engine}/client.d.ts +59 -60
- package/dist/packages/{local-service → engine}/client.js +137 -145
- package/dist/packages/{compiler → engine/compile}/artifact-counts.js +1 -1
- package/dist/packages/{compiler → engine/compile}/compiled-paths.d.ts +9 -2
- package/dist/packages/{compiler → engine/compile}/compiled-paths.js +30 -15
- package/dist/packages/{compiler → engine/compile}/compiled-pipeline.d.ts +12 -1
- package/dist/packages/{compiler → engine/compile}/compiled-pipeline.js +39 -9
- package/dist/packages/{compiler → engine/compile}/compiled-schema.d.ts +2 -2
- package/dist/packages/{compiler → engine/compile}/compiled-schema.js +4 -4
- package/dist/packages/{compiler → engine/compile}/compiled-stage-plan.d.ts +1 -1
- package/dist/packages/{compiler → engine/compile}/compiled-stage-plan.js +8 -4
- package/dist/packages/{compiler → engine/compile}/compiled-stage-runner.d.ts +1 -1
- package/dist/packages/{compiler → engine/compile}/compiled-stage-runner.js +3 -3
- package/dist/packages/{compiler → engine/compile}/compiled-target.d.ts +3 -3
- package/dist/packages/{compiler → engine/compile}/compiled-target.js +3 -3
- package/dist/packages/{compiler → engine/compile}/discovery.js +1 -1
- package/dist/packages/{compiler → engine/compile}/index.d.ts +1 -0
- package/dist/packages/{compiler → engine/compile}/index.js +1 -0
- package/dist/packages/{compiler → engine/compile}/lib/schema.d.ts +26 -31
- package/dist/packages/{compiler → engine/compile}/lib/schema.js +2 -13
- package/dist/packages/engine/compile/method-runs.d.ts +14 -0
- package/dist/packages/{compiler → engine/compile}/method-runs.js +5 -6
- package/dist/packages/{compiler → engine/compile}/reset.js +3 -1
- package/dist/packages/{compiler → engine/compile}/runtime-acceptance.js +17 -14
- package/dist/packages/{compiler → engine/compile}/runtime-contracts.js +0 -3
- package/dist/packages/{compiler → engine/compile}/runtime-prompt.js +1 -1
- package/dist/packages/{compiler → engine/compile}/runtime-reconcile.d.ts +1 -1
- package/dist/packages/{compiler → engine/compile}/runtime-reconcile.js +12 -10
- package/dist/packages/{compiler → engine/compile}/runtime-runs.d.ts +1 -2
- package/dist/packages/{compiler → engine/compile}/runtime-runs.js +3 -43
- package/dist/packages/{compiler → engine/compile}/runtime-types.d.ts +1 -5
- package/dist/packages/{compiler → engine/compile}/runtime.d.ts +2 -2
- package/dist/packages/{compiler → engine/compile}/runtime.js +1 -1
- package/dist/packages/engine/compile/source-files.d.ts +46 -0
- package/dist/packages/engine/compile/source-files.js +149 -0
- package/dist/packages/engine/compile/state-artifacts.d.ts +9 -0
- package/dist/packages/{compiler → engine/compile}/state-artifacts.js +4 -3
- package/dist/packages/{compiler → engine/compile}/state-health.js +2 -2
- package/dist/packages/{compiler → engine/compile}/state-io.d.ts +3 -2
- package/dist/packages/{compiler → engine/compile}/state-io.js +13 -7
- package/dist/packages/{compiler → engine/compile}/state-paths.d.ts +2 -1
- package/dist/packages/engine/compile/state-paths.js +16 -0
- package/dist/packages/engine/compile/state-view.d.ts +5 -0
- package/dist/packages/{compiler → engine/compile}/state-view.js +20 -30
- package/dist/packages/engine/compile/state.d.ts +7 -0
- package/dist/packages/{compiler → engine/compile}/state.js +3 -3
- package/dist/packages/{compiler → engine/compile}/validate-compiled.js +2 -2
- package/dist/packages/{compiler → engine/compile}/validate.d.ts +1 -1
- package/dist/packages/{compiler → engine/compile}/validate.js +3 -3
- package/dist/packages/engine/connection-config.d.ts +38 -0
- package/dist/packages/engine/connection-config.js +75 -0
- package/dist/packages/{execution → engine/execution}/lib/schema.d.ts +52 -72
- package/dist/packages/{execution → engine/execution}/lib/schema.js +3 -3
- package/dist/packages/engine/index.d.ts +22 -0
- package/dist/packages/engine/index.js +15 -0
- package/dist/packages/engine/instance-paths.d.ts +100 -0
- package/dist/packages/engine/instance-paths.js +165 -0
- package/dist/packages/{local-service → engine}/lib/schema.d.ts +392 -2408
- package/dist/packages/{local-service → engine}/lib/schema.js +164 -76
- package/dist/packages/{local-service → engine}/native-run-handlers.d.ts +7 -5
- package/dist/packages/{local-service → engine}/native-run-handlers.js +71 -27
- package/dist/packages/engine/preparation-store.d.ts +104 -0
- package/dist/packages/engine/preparation-store.js +194 -0
- package/dist/packages/{local-service → engine}/readiness-check-draft.d.ts +2 -2
- package/dist/packages/engine/routes.d.ts +78 -0
- package/dist/packages/engine/routes.js +92 -0
- package/dist/packages/{local-service → engine}/run-observability.d.ts +3 -3
- package/dist/packages/{local-service → engine}/run-observability.js +25 -24
- package/dist/packages/engine/runtime-caches.d.ts +76 -0
- package/dist/packages/engine/runtime-caches.js +191 -0
- package/dist/packages/engine/runtime-event-applier.d.ts +12 -0
- package/dist/packages/engine/runtime-event-applier.js +177 -0
- package/dist/packages/engine/runtime-persistence.d.ts +47 -0
- package/dist/packages/engine/runtime-persistence.js +137 -0
- package/dist/packages/engine/runtime-proposal-helpers.d.ts +35 -0
- package/dist/packages/engine/runtime-proposal-helpers.js +251 -0
- package/dist/packages/engine/runtime-resource-builders.d.ts +52 -0
- package/dist/packages/engine/runtime-resource-builders.js +149 -0
- package/dist/packages/engine/runtime.d.ts +318 -0
- package/dist/packages/{local-service → engine}/runtime.js +835 -1011
- package/dist/packages/{local-service → engine}/server.d.ts +15 -0
- package/dist/packages/engine/server.js +1257 -0
- package/dist/packages/engine/service-registry.d.ts +47 -0
- package/dist/packages/engine/service-registry.js +137 -0
- package/dist/packages/{testing → engine/verify}/lib/schema.d.ts +11 -11
- package/dist/packages/{testing → engine/verify}/lib/schema.js +3 -3
- package/dist/packages/{testing → engine/verify}/readiness-check-run.d.ts +9 -16
- package/dist/packages/{testing → engine/verify}/readiness-check-run.js +38 -94
- package/dist/packages/{testing → engine/verify}/test-execution.js +6 -6
- package/dist/packages/{testing → engine/verify}/test-paths.js +5 -4
- package/dist/packages/{testing → engine/verify}/test-sandbox.d.ts +0 -1
- package/dist/packages/{testing → engine/verify}/test-sandbox.js +17 -33
- package/dist/packages/{testing → engine/verify}/test-specs.js +1 -1
- package/dist/packages/{testing → engine/verify}/test-targets.d.ts +1 -1
- package/dist/packages/{testing → engine/verify}/test-targets.js +9 -9
- package/dist/packages/{testing → engine/verify}/test.d.ts +1 -1
- package/dist/packages/{testing → engine/verify}/test.js +1 -1
- package/dist/packages/{method-authoring → methods/authoring}/method-authoring.d.ts +12 -4
- package/dist/packages/{method-authoring → methods/authoring}/method-authoring.js +70 -7
- package/dist/packages/{method-authoring → methods/authoring}/method-edit-session.d.ts +2 -2
- package/dist/packages/{method-authoring → methods/authoring}/method-improvement.d.ts +4 -4
- package/dist/packages/{method-authoring → methods/authoring}/method-improvement.js +16 -10
- package/dist/packages/{method-package → methods/package}/builtin-compiled-method.d.ts +4 -5
- package/dist/packages/{method-package → methods/package}/builtin-compiled-method.js +10 -16
- package/dist/packages/{method-package → methods/package}/context-interface.d.ts +5 -41
- package/dist/packages/{method-package → methods/package}/context-interface.js +3 -25
- package/dist/packages/{method-package → methods/package}/interf-method-package.d.ts +4 -4
- package/dist/packages/{method-package → methods/package}/interf-method-package.js +24 -35
- package/dist/packages/{method-package → methods/package}/lib/package-root.js +2 -2
- package/dist/packages/{method-package → methods/package}/local-methods.d.ts +18 -8
- package/dist/packages/{method-package → methods/package}/local-methods.js +64 -45
- package/dist/packages/{method-package → methods/package}/method-definitions.d.ts +16 -36
- package/dist/packages/{method-package → methods/package}/method-definitions.js +53 -40
- package/dist/packages/{method-package → methods/package}/method-helpers.d.ts +2 -14
- package/dist/packages/{method-package → methods/package}/method-helpers.js +12 -46
- package/dist/packages/{method-package → methods/package}/method-review-paths.d.ts +1 -1
- package/dist/packages/{method-package → methods/package}/method-review-paths.js +1 -1
- package/dist/packages/{method-package → methods/package}/method-stage-runner.d.ts +4 -9
- package/dist/packages/{method-package → methods/package}/method-stage-runner.js +3 -31
- package/dist/packages/methods/package/user-methods.d.ts +17 -0
- package/dist/packages/methods/package/user-methods.js +77 -0
- package/dist/packages/{project-model → project}/index.d.ts +0 -1
- package/dist/packages/{project-model → project}/index.js +0 -1
- package/dist/packages/{project-model → project}/interf-bootstrap.d.ts +1 -1
- package/dist/packages/{project-model → project}/interf-bootstrap.js +1 -1
- package/dist/packages/{project-model → project}/interf-detect.d.ts +8 -3
- package/dist/packages/{project-model → project}/interf-detect.js +38 -38
- package/dist/packages/project/interf-scaffold.d.ts +3 -0
- package/dist/packages/{project-model → project}/interf-scaffold.js +30 -39
- package/dist/packages/{project-model → project}/lib/schema.d.ts +2 -2
- package/dist/packages/{project-model → project}/lib/schema.js +39 -2
- package/dist/packages/project/preparation-entries.d.ts +11 -0
- package/dist/packages/{project-model → project}/preparation-entries.js +14 -14
- package/dist/packages/{project-model → project}/source-config.d.ts +12 -12
- package/dist/packages/{project-model → project}/source-config.js +81 -53
- package/dist/packages/{project-model → project}/source-folders.d.ts +5 -5
- package/dist/packages/{project-model → project}/source-folders.js +16 -16
- package/package.json +8 -8
- package/CHANGELOG.md +0 -93
- package/LICENSE +0 -183
- package/dist/cli/commands/action-input-cli.d.ts +0 -25
- package/dist/cli/commands/action-input-cli.js +0 -73
- package/dist/cli/commands/control-path.d.ts +0 -11
- package/dist/cli/commands/control-path.js +0 -72
- package/dist/cli/commands/create-method-wizard.d.ts +0 -64
- package/dist/cli/commands/create-method-wizard.js +0 -434
- package/dist/cli/commands/create.d.ts +0 -6
- package/dist/cli/commands/create.js +0 -183
- package/dist/cli/commands/default.d.ts +0 -2
- package/dist/cli/commands/default.js +0 -39
- package/dist/cli/commands/executor-flow.d.ts +0 -29
- package/dist/cli/commands/executor-flow.js +0 -163
- package/dist/cli/commands/init.d.ts +0 -26
- package/dist/cli/commands/init.js +0 -771
- package/dist/cli/commands/list.d.ts +0 -2
- package/dist/cli/commands/list.js +0 -30
- package/dist/cli/commands/preparation-action.d.ts +0 -8
- package/dist/cli/commands/preparation-action.js +0 -29
- package/dist/cli/commands/preparation-picker.d.ts +0 -5
- package/dist/cli/commands/preparation-picker.js +0 -36
- package/dist/cli/commands/preparation-selection.d.ts +0 -6
- package/dist/cli/commands/preparation-selection.js +0 -11
- package/dist/cli/commands/service-action-flow.d.ts +0 -9
- package/dist/cli/commands/service-action-flow.js +0 -19
- package/dist/cli/commands/source-config-wizard.d.ts +0 -51
- package/dist/cli/commands/source-config-wizard.js +0 -670
- package/dist/cli/commands/test.d.ts +0 -17
- package/dist/cli/commands/test.js +0 -188
- package/dist/packages/agents/lib/detection.d.ts +0 -7
- package/dist/packages/agents/lib/user-config.js +0 -16
- package/dist/packages/compiler/method-runs.d.ts +0 -15
- package/dist/packages/compiler/raw-snapshot.d.ts +0 -49
- package/dist/packages/compiler/raw-snapshot.js +0 -101
- package/dist/packages/compiler/state-artifacts.d.ts +0 -8
- package/dist/packages/compiler/state-paths.js +0 -13
- package/dist/packages/compiler/state-view.d.ts +0 -4
- package/dist/packages/compiler/state.d.ts +0 -7
- package/dist/packages/local-service/index.d.ts +0 -18
- package/dist/packages/local-service/index.js +0 -13
- package/dist/packages/local-service/routes.d.ts +0 -32
- package/dist/packages/local-service/routes.js +0 -37
- package/dist/packages/local-service/runtime.d.ts +0 -133
- package/dist/packages/local-service/server.js +0 -627
- package/dist/packages/method-package/index.d.ts +0 -11
- package/dist/packages/method-package/index.js +0 -11
- package/dist/packages/method-package/method-stage-policy.d.ts +0 -5
- package/dist/packages/method-package/method-stage-policy.js +0 -31
- package/dist/packages/project-model/interf-scaffold.d.ts +0 -3
- package/dist/packages/project-model/preparation-entries.d.ts +0 -11
- package/dist/packages/project-model/project-paths.d.ts +0 -12
- package/dist/packages/project-model/project-paths.js +0 -33
- package/dist/packages/shared/filesystem.d.ts +0 -2
- package/dist/packages/shared/filesystem.js +0 -55
- /package/dist/compiler-ui/_next/static/{84FaeF3EzBF9kKTMjSEVN → a3UiUF0DiMEbfWy_0gihg}/_buildManifest.js +0 -0
- /package/dist/compiler-ui/_next/static/{84FaeF3EzBF9kKTMjSEVN → a3UiUF0DiMEbfWy_0gihg}/_clientMiddlewareManifest.js +0 -0
- /package/dist/compiler-ui/_next/static/{84FaeF3EzBF9kKTMjSEVN → a3UiUF0DiMEbfWy_0gihg}/_ssgManifest.js +0 -0
- /package/dist/packages/{shared → contracts/utils}/file-types.d.ts +0 -0
- /package/dist/packages/{shared → contracts/utils}/file-types.js +0 -0
- /package/dist/packages/{shared → contracts/utils}/logger.d.ts +0 -0
- /package/dist/packages/{shared → contracts/utils}/logger.js +0 -0
- /package/dist/packages/{shared → contracts/utils}/naming.d.ts +0 -0
- /package/dist/packages/{shared → contracts/utils}/naming.js +0 -0
- /package/dist/packages/{shared → contracts/utils}/parse.d.ts +0 -0
- /package/dist/packages/{shared → contracts/utils}/parse.js +0 -0
- /package/dist/packages/{shared → contracts/utils}/path-guards.d.ts +0 -0
- /package/dist/packages/{shared → contracts/utils}/path-guards.js +0 -0
- /package/dist/packages/{local-service → engine}/action-values.d.ts +0 -0
- /package/dist/packages/{local-service → engine}/action-values.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/agents.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/agents.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/args.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/args.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/chart-guidance.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/chart-guidance.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/compiled-bootstrap.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/constants.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/constants.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/execution-profile.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/execution-profile.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/execution.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/execution.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/executors.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/logs.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/logs.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/preflight.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/preflight.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/render.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/render.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/schema.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/schema.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/status.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/status.js +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/types.d.ts +0 -0
- /package/dist/packages/{agents → engine/agents}/lib/types.js +0 -0
- /package/dist/packages/{compiler → engine/compile}/artifact-counts.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/compiled-compile.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/compiled-compile.js +0 -0
- /package/dist/packages/{compiler → engine/compile}/discovery.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/method-primitives.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/method-primitives.js +0 -0
- /package/dist/packages/{compiler → engine/compile}/reset.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-acceptance.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-contracts.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-inventory.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-inventory.js +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-paths.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-paths.js +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-prompt.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/runtime-types.js +0 -0
- /package/dist/packages/{compiler → engine/compile}/state-health.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/validate-compiled.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/validate-helpers.d.ts +0 -0
- /package/dist/packages/{compiler → engine/compile}/validate-helpers.js +0 -0
- /package/dist/packages/{execution → engine/execution}/adapters.d.ts +0 -0
- /package/dist/packages/{execution → engine/execution}/adapters.js +0 -0
- /package/dist/packages/{execution → engine/execution}/events.d.ts +0 -0
- /package/dist/packages/{execution → engine/execution}/events.js +0 -0
- /package/dist/packages/{execution → engine/execution}/index.d.ts +0 -0
- /package/dist/packages/{execution → engine/execution}/index.js +0 -0
- /package/dist/packages/{local-service → engine}/readiness-check-draft.js +0 -0
- /package/dist/packages/{testing → engine/verify}/index.d.ts +0 -0
- /package/dist/packages/{testing → engine/verify}/index.js +0 -0
- /package/dist/packages/{testing → engine/verify}/test-execution.d.ts +0 -0
- /package/dist/packages/{testing → engine/verify}/test-paths.d.ts +0 -0
- /package/dist/packages/{testing → engine/verify}/test-profile-presets.d.ts +0 -0
- /package/dist/packages/{testing → engine/verify}/test-profile-presets.js +0 -0
- /package/dist/packages/{testing → engine/verify}/test-specs.d.ts +0 -0
- /package/dist/packages/{testing → engine/verify}/test-types.d.ts +0 -0
- /package/dist/packages/{testing → engine/verify}/test-types.js +0 -0
- /package/dist/packages/{method-authoring → methods/authoring}/index.d.ts +0 -0
- /package/dist/packages/{method-authoring → methods/authoring}/index.js +0 -0
- /package/dist/packages/{method-authoring → methods/authoring}/lib/method-edit-utils.d.ts +0 -0
- /package/dist/packages/{method-authoring → methods/authoring}/lib/method-edit-utils.js +0 -0
- /package/dist/packages/{method-authoring → methods/authoring}/method-edit-session.js +0 -0
- /package/dist/packages/{method-package → methods/package}/lib/package-root.d.ts +0 -0
- /package/dist/packages/{project-model → project}/interf.d.ts +0 -0
- /package/dist/packages/{project-model → project}/interf.js +0 -0
|
@@ -1,714 +1,240 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
2
|
-
import {
|
|
3
|
-
import { CompileRunSchema, } from "
|
|
4
|
-
import { createRunEventId, createRunEventTimestamp, } from "
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { existsSync, mkdirSync, rmSync, statSync, } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { CompileRunSchema, } from "./execution/lib/schema.js";
|
|
4
|
+
import { createRunEventId, createRunEventTimestamp, } from "./execution/events.js";
|
|
5
|
+
import { loadState, } from "./compile/state.js";
|
|
6
|
+
import { actionProposalPath, actionProposalsRoot, byCreatedAtDesc, compileRunPath, compileRunsRoot, listJsonFiles, localJobPath, localJobsRoot, newestFirst, readActionProposalAt, readCompileRunAt, readLocalJobRunAt, readRuntimeRunHistory, readVerifyRunAt, verifyRunPath, verifyRunsRoot, timestampKey, writeJsonFile, } from "./runtime-persistence.js";
|
|
7
|
+
import { MethodListingCache, MtimeListingCache, ReadinessCache, RunListingCache, } from "./runtime-caches.js";
|
|
8
|
+
import { applyEventToCompileRun, applyEventToLocalJob, } from "./runtime-event-applier.js";
|
|
9
|
+
import { buildMethodResource, buildPreparationResource, createRunId, logsForRuntimeRun, logsForStageRun, proofForStage, readinessStateToPreparationReadiness, readinessSummaryForStatus, readinessTargetResult, stageArtifactRefs, } from "./runtime-resource-builders.js";
|
|
10
|
+
import { ACTION_PLANNER_CLARIFICATION_MESSAGE, actionAssistantMessage, actionCommandPreview, actionTypeFromValues, actionValueMethodTaskPrompt, configuredAgentName, createActionProposalId, detachMethodFromPreparation, detectedExecutorOptions, directServiceEndpointForAction, hasCompiledTestTarget, methodAuthoringHintFromPrompt, methodAuthoringPromptFallback, methodIdForProposal, methodLabelFromId, numberValue, requireSelectedMethod, sanitizeActionProposalPlan, stringValue, testModeFromValues, } from "./runtime-proposal-helpers.js";
|
|
8
11
|
import { ReadinessStateSchema, } from "../contracts/lib/schema.js";
|
|
9
|
-
import { discoverSourceFiles, } from "
|
|
10
|
-
import { resetCompiledGeneratedState, } from "
|
|
11
|
-
import { ensurePortableContextScaffold, readInterfConfig, } from "../project
|
|
12
|
-
import { findSourcePreparationConfig, fingerprintReadinessChecks, listSourcePreparationConfigs, loadSourceFolderConfig, DEFAULT_METHOD_ID, methodIdForSourcePreparationConfig, resolveConfiguredSourceFolderPath, resolveSourcePreparationPath, removeSourcePreparationConfig, saveSourceFolderConfig, syncCompiledInterfConfigFromSourcePreparationConfig, upsertSourcePreparationConfig, } from "../project
|
|
13
|
-
import { listSourceFolderChoices, } from "../project
|
|
14
|
-
import {
|
|
15
|
-
import { getCompiledMethod, listCompiledMethodChoices, } from "../
|
|
16
|
-
import { contextInterfaceArtifactPath, } from "../
|
|
17
|
-
import { methodDefinitionPath, resolveMethodPackageSourcePath, } from "../
|
|
18
|
-
import { seedLocalMethodPackageFromBase, } from "../
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import { loadUserConfig, saveUserConfig, } from "
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
12
|
+
import { discoverSourceFiles, } from "./compile/discovery.js";
|
|
13
|
+
import { resetCompiledGeneratedState, } from "./compile/reset.js";
|
|
14
|
+
import { ensurePortableContextScaffold, readInterfConfig, } from "../project/interf.js";
|
|
15
|
+
import { findSourcePreparationConfig, fingerprintReadinessChecks, listSourcePreparationConfigs, loadSourceFolderConfig, DEFAULT_METHOD_ID, methodIdForSourcePreparationConfig, resolveConfiguredSourceFolderPath, resolveSourcePreparationPath, removeSourcePreparationConfig, saveSourceFolderConfig, syncCompiledInterfConfigFromSourcePreparationConfig, upsertSourcePreparationConfig, } from "../project/source-config.js";
|
|
16
|
+
import { listSourceFolderChoices, } from "../project/source-folders.js";
|
|
17
|
+
import { asPreparationDataDir, preparationPortableContextPath, userMethodsRoot, preparationConfigPath, preparationMethodPackagePath, preparationMethodsRoot, } from "../contracts/lib/preparation-paths.js";
|
|
18
|
+
import { getCompiledMethod, listCompiledMethodChoices, } from "../methods/package/method-definitions.js";
|
|
19
|
+
import { contextInterfaceArtifactPath, } from "../methods/package/context-interface.js";
|
|
20
|
+
import { methodDefinitionPath, resolveMethodPackageSourcePath, } from "../methods/package/local-methods.js";
|
|
21
|
+
import { seedLocalMethodPackageFromBase, } from "../methods/package/interf-method-package.js";
|
|
22
|
+
import { PACKAGE_ROOT } from "../methods/package/lib/package-root.js";
|
|
23
|
+
import { resolveAgent, detectAgents, supportsAutomatedRuns, } from "./agents/lib/detection.js";
|
|
24
|
+
import { loadUserConfig, saveUserConfig, } from "./agents/lib/user-config.js";
|
|
25
|
+
import { loadAgentsRegistry, registerCustomAgent, unregisterCustomAgent, patchRoleMap, setActiveAgent, } from "./agents/registry.js";
|
|
26
|
+
import { readSavedReadinessCheckRun, } from "./verify/readiness-check-run.js";
|
|
27
|
+
import { createCompiledTestTarget, } from "./verify/test-targets.js";
|
|
28
|
+
import { ActionProposalApprovalRequestSchema, ActionProposalCreateRequestSchema, ActionProposalPlanSchema, ActionProposalResourceSchema, ActionProposalTypeSchema, CompileRunCreateRequestSchema, CompileRunResourceSchema, LocalExecutorStatusSchema, LocalExecutorSelectRequestSchema, LocalServiceHealthSchema, LocalRunHandlerResultSchema, LocalJobEventAppendRequestSchema, LocalJobRunCreateRequestSchema, LocalJobRunResourceSchema, SourceFileResourceSchema, WorkspaceFileResourceSchema, PortableContextResourceSchema, PreparationSetupCreateRequestSchema, PreparationSetupResultSchema, MethodChangeCreateRequestSchema, MethodChangeResultSchema, PreparationChangeCreateRequestSchema, PreparationChangeResultSchema, ReadinessCheckDraftCreateRequestSchema, ReadinessCheckDraftResultSchema, ResetRequestSchema, ResetResultSchema, ServiceRegistryWorkspaceSchema, VerifyRunCreateRequestSchema, VerifyRunResourceSchema, MethodAuthoringCreateRequestSchema, MethodAuthoringResultSchema, } from "./lib/schema.js";
|
|
25
29
|
import { buildLocalServiceUrl, } from "./routes.js";
|
|
26
|
-
import {
|
|
27
|
-
import { compileRunToObservability, jobRunToObservability,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return `action_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
33
|
-
}
|
|
34
|
-
const ACTION_PLANNER_CLARIFICATION_MESSAGE = "I can help with this Interf Workspace. Ask a question about Interf, or ask me to create a Preparation, prepare, check readiness, improve, or draft a Method and I will prepare an approval proposal.";
|
|
35
|
-
function readJsonFile(filePath) {
|
|
36
|
-
try {
|
|
37
|
-
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function writeJsonFile(filePath, value) {
|
|
44
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
45
|
-
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
46
|
-
}
|
|
47
|
-
function sanitizeActionProposalPlan(value) {
|
|
48
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
49
|
-
return value;
|
|
50
|
-
const plan = { ...value };
|
|
51
|
-
for (const key of ["preparation", "method", "title", "summary", "assistant_message", "command_preview"]) {
|
|
52
|
-
const current = plan[key];
|
|
53
|
-
if (current === null || current === undefined) {
|
|
54
|
-
delete plan[key];
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (typeof current === "string" && current.trim().length === 0) {
|
|
58
|
-
delete plan[key];
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return plan;
|
|
62
|
-
}
|
|
63
|
-
function compileRunsRoot(compiledPath) {
|
|
64
|
-
return join(compiledRuntimeRoot(compiledPath), "compile-runs");
|
|
65
|
-
}
|
|
66
|
-
function compileRunPath(compiledPath, runId) {
|
|
67
|
-
return join(compileRunsRoot(compiledPath), `${runId}.json`);
|
|
68
|
-
}
|
|
69
|
-
function testRunsRoot(compiledPath) {
|
|
70
|
-
return join(testRootForCompiled(compiledPath), "service-runs");
|
|
71
|
-
}
|
|
72
|
-
function testRunPath(compiledPath, runId) {
|
|
73
|
-
return join(testRunsRoot(compiledPath), `${runId}.json`);
|
|
74
|
-
}
|
|
75
|
-
function localJobsRoot(rootPath) {
|
|
76
|
-
return join(rootPath, "interf", ".service", "jobs");
|
|
77
|
-
}
|
|
78
|
-
function localJobPath(rootPath, runId) {
|
|
79
|
-
return join(localJobsRoot(rootPath), `${runId}.json`);
|
|
80
|
-
}
|
|
81
|
-
function actionProposalsRoot(rootPath) {
|
|
82
|
-
return join(rootPath, "interf", ".service", "action-proposals");
|
|
83
|
-
}
|
|
84
|
-
function actionProposalPath(rootPath, proposalId) {
|
|
85
|
-
return join(actionProposalsRoot(rootPath), `${proposalId}.json`);
|
|
86
|
-
}
|
|
87
|
-
function listJsonFiles(dirPath) {
|
|
88
|
-
if (!existsSync(dirPath))
|
|
89
|
-
return [];
|
|
90
|
-
try {
|
|
91
|
-
if (!statSync(dirPath).isDirectory())
|
|
92
|
-
return [];
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return [];
|
|
96
|
-
}
|
|
97
|
-
return readdirSync(dirPath)
|
|
98
|
-
.filter((entry) => entry.endsWith(".json"))
|
|
99
|
-
.map((entry) => join(dirPath, entry));
|
|
100
|
-
}
|
|
101
|
-
function readCompileRunAt(filePath) {
|
|
102
|
-
const parsed = CompileRunSchema.safeParse(readJsonFile(filePath));
|
|
103
|
-
return parsed.success ? parsed.data : null;
|
|
104
|
-
}
|
|
105
|
-
function readRuntimeRunHistory(compiledPath) {
|
|
106
|
-
const historyPath = compiledRuntimeRunHistoryPath(compiledPath);
|
|
107
|
-
if (!existsSync(historyPath))
|
|
108
|
-
return [];
|
|
109
|
-
try {
|
|
110
|
-
return readFileSync(historyPath, "utf8")
|
|
111
|
-
.split(/\r?\n/)
|
|
112
|
-
.map((line) => line.trim())
|
|
113
|
-
.filter((line) => line.length > 0)
|
|
114
|
-
.map((line) => {
|
|
115
|
-
try {
|
|
116
|
-
return RuntimeRunSchema.safeParse(JSON.parse(line));
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
return { success: false };
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
.filter((parsed) => parsed.success)
|
|
123
|
-
.map((parsed) => parsed.data);
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return [];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function readTestRunAt(filePath) {
|
|
130
|
-
const parsed = TestRunResourceSchema.safeParse(readJsonFile(filePath));
|
|
131
|
-
return parsed.success ? parsed.data : null;
|
|
132
|
-
}
|
|
133
|
-
function readLocalJobRunAt(filePath) {
|
|
134
|
-
const parsed = LocalJobRunResourceSchema.safeParse(readJsonFile(filePath));
|
|
135
|
-
return parsed.success ? parsed.data : null;
|
|
136
|
-
}
|
|
137
|
-
function readActionProposalAt(filePath) {
|
|
138
|
-
const parsed = ActionProposalResourceSchema.safeParse(readJsonFile(filePath));
|
|
139
|
-
return parsed.success ? parsed.data : null;
|
|
140
|
-
}
|
|
141
|
-
function newestFirst(items) {
|
|
142
|
-
return [...items].sort((left, right) => {
|
|
143
|
-
const leftTime = Date.parse(left.started_at ?? left.finished_at ?? "");
|
|
144
|
-
const rightTime = Date.parse(right.started_at ?? right.finished_at ?? "");
|
|
145
|
-
return (Number.isFinite(rightTime) ? rightTime : 0) - (Number.isFinite(leftTime) ? leftTime : 0);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
function newestJobFirst(items) {
|
|
149
|
-
return [...items].sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at));
|
|
150
|
-
}
|
|
151
|
-
function newestActionProposalFirst(items) {
|
|
152
|
-
return [...items].sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at));
|
|
153
|
-
}
|
|
154
|
-
function newestCompileFirst(items) {
|
|
155
|
-
return [...items].sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at));
|
|
156
|
-
}
|
|
157
|
-
function configuredAgentName() {
|
|
158
|
-
const config = loadUserConfig();
|
|
159
|
-
if (!config)
|
|
160
|
-
return null;
|
|
161
|
-
const configured = AGENTS.find((agent) => agent.command === config.agentCommand) ??
|
|
162
|
-
AGENTS.find((agent) => agent.name === config.agent) ??
|
|
163
|
-
AGENTS.find((agent) => agent.displayName === config.agent);
|
|
164
|
-
return configured?.name ?? null;
|
|
165
|
-
}
|
|
166
|
-
function detectedExecutorOptions(currentAgentName) {
|
|
167
|
-
return detectAgents()
|
|
168
|
-
.filter(supportsAutomatedRuns)
|
|
169
|
-
.map((agent) => ({
|
|
170
|
-
name: agent.name,
|
|
171
|
-
display_name: agent.displayName,
|
|
172
|
-
command: agent.command,
|
|
173
|
-
current: agent.name === currentAgentName,
|
|
174
|
-
}));
|
|
175
|
-
}
|
|
176
|
-
function stageArtifactRefs(stageId, artifacts) {
|
|
177
|
-
return (artifacts ?? []).map((path) => ({
|
|
178
|
-
path,
|
|
179
|
-
role: "output",
|
|
180
|
-
stage_id: stageId,
|
|
181
|
-
label: path,
|
|
182
|
-
}));
|
|
183
|
-
}
|
|
184
|
-
function proofForStage(options) {
|
|
185
|
-
return {
|
|
186
|
-
id: `${options.runId}-${options.stageId}-proof`,
|
|
187
|
-
run_id: options.runId,
|
|
188
|
-
stage_id: options.stageId,
|
|
189
|
-
generated_at: options.stageState.finished_at ?? new Date().toISOString(),
|
|
190
|
-
summary: options.summary ?? `${options.stageId} produced stage evidence.`,
|
|
191
|
-
files_processed: options.stageState.counts?.source_total,
|
|
192
|
-
artifacts: options.artifacts,
|
|
193
|
-
checks: [
|
|
194
|
-
{
|
|
195
|
-
id: `${options.stageId}-status`,
|
|
196
|
-
label: "stage completed",
|
|
197
|
-
ok: options.stageState.status === "succeeded",
|
|
198
|
-
...(options.stageState.status === "succeeded"
|
|
199
|
-
? {}
|
|
200
|
-
: { detail: options.stageState.summary ?? "Stage did not complete successfully." }),
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
id: `${options.stageId}-artifacts`,
|
|
204
|
-
label: "artifacts recorded",
|
|
205
|
-
ok: options.artifacts.length > 0,
|
|
206
|
-
...(options.artifacts.length > 0
|
|
207
|
-
? {}
|
|
208
|
-
: { detail: "No stage artifacts were recorded." }),
|
|
209
|
-
},
|
|
210
|
-
],
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
function logsForStageRun(stageState) {
|
|
214
|
-
const runId = stageState?.run_id;
|
|
215
|
-
if (!runId)
|
|
216
|
-
return undefined;
|
|
217
|
-
return {
|
|
218
|
-
prompt_path: `.interf/runtime/logs/${runId}.prompt.txt`,
|
|
219
|
-
event_stream_path: `.interf/runtime/logs/${runId}.events.ndjson`,
|
|
220
|
-
status_path: `.interf/runtime/logs/${runId}.status.log`,
|
|
221
|
-
contract_path: `.interf/runtime/logs/${runId}.stage-contract.json`,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
function logsForRuntimeRun(run) {
|
|
225
|
-
if (!run)
|
|
226
|
-
return undefined;
|
|
227
|
-
return {
|
|
228
|
-
...(run.logs?.prompt_path ? { prompt_path: run.logs.prompt_path } : {}),
|
|
229
|
-
...(run.logs?.event_stream_path ? { event_stream_path: run.logs.event_stream_path } : {}),
|
|
230
|
-
...(run.logs?.status_path ? { status_path: run.logs.status_path } : {}),
|
|
231
|
-
contract_path: run.contract_path,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
function timestampKey(value) {
|
|
235
|
-
const parsed = Date.parse(value ?? "");
|
|
236
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
237
|
-
}
|
|
238
|
-
function applyEventToCompileRun(run, event) {
|
|
239
|
-
const now = event.timestamp;
|
|
240
|
-
const stageFor = (stageId) => {
|
|
241
|
-
const existing = run.stages.find((stage) => stage.stage_id === stageId);
|
|
242
|
-
if (existing)
|
|
243
|
-
return existing;
|
|
244
|
-
const created = {
|
|
245
|
-
run_id: run.run_id,
|
|
246
|
-
stage_id: stageId,
|
|
247
|
-
status: "queued",
|
|
248
|
-
artifacts: [],
|
|
249
|
-
};
|
|
250
|
-
run.stages.push(created);
|
|
251
|
-
return created;
|
|
252
|
-
};
|
|
253
|
-
const updateStage = (stageId, patch) => {
|
|
254
|
-
const current = stageFor(stageId);
|
|
255
|
-
Object.assign(current, patch);
|
|
256
|
-
};
|
|
257
|
-
switch (event.type) {
|
|
258
|
-
case "run.started":
|
|
259
|
-
return {
|
|
260
|
-
...run,
|
|
261
|
-
status: "running",
|
|
262
|
-
started_at: run.started_at ?? now,
|
|
263
|
-
events: [...run.events, event],
|
|
264
|
-
};
|
|
265
|
-
case "stage.started":
|
|
266
|
-
updateStage(event.stage_id, {
|
|
267
|
-
status: "running",
|
|
268
|
-
started_at: now,
|
|
269
|
-
stage_index: event.stage_index,
|
|
270
|
-
stage_total: event.stage_total,
|
|
271
|
-
});
|
|
272
|
-
break;
|
|
273
|
-
case "artifact.written": {
|
|
274
|
-
const stage = stageFor(event.stage_id);
|
|
275
|
-
updateStage(event.stage_id, {
|
|
276
|
-
artifacts: uniqueArtifacts([...(stage.artifacts ?? []), event.artifact]),
|
|
277
|
-
});
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
case "proof.updated":
|
|
281
|
-
if (event.stage_id) {
|
|
282
|
-
updateStage(event.stage_id, {
|
|
283
|
-
latest_proof: event.proof,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
return {
|
|
287
|
-
...run,
|
|
288
|
-
latest_proof: event.proof,
|
|
289
|
-
events: [...run.events, event],
|
|
290
|
-
};
|
|
291
|
-
case "log.appended":
|
|
292
|
-
break;
|
|
293
|
-
case "stage.passed":
|
|
294
|
-
updateStage(event.stage_id, {
|
|
295
|
-
status: "succeeded",
|
|
296
|
-
finished_at: now,
|
|
297
|
-
summary: event.summary ?? null,
|
|
298
|
-
failure: null,
|
|
299
|
-
});
|
|
300
|
-
break;
|
|
301
|
-
case "stage.failed":
|
|
302
|
-
updateStage(event.stage_id, {
|
|
303
|
-
status: "failed",
|
|
304
|
-
finished_at: now,
|
|
305
|
-
summary: event.error,
|
|
306
|
-
failure: event.error,
|
|
307
|
-
});
|
|
308
|
-
break;
|
|
309
|
-
case "run.completed":
|
|
310
|
-
return {
|
|
311
|
-
...run,
|
|
312
|
-
status: "succeeded",
|
|
313
|
-
finished_at: run.finished_at ?? now,
|
|
314
|
-
events: [...run.events, event],
|
|
315
|
-
};
|
|
316
|
-
case "run.failed":
|
|
317
|
-
return {
|
|
318
|
-
...run,
|
|
319
|
-
status: "failed",
|
|
320
|
-
finished_at: run.finished_at ?? now,
|
|
321
|
-
events: [...run.events, event],
|
|
322
|
-
};
|
|
323
|
-
case "readiness.updated":
|
|
324
|
-
return {
|
|
325
|
-
...run,
|
|
326
|
-
readiness: event.readiness,
|
|
327
|
-
events: [...run.events, event],
|
|
328
|
-
};
|
|
329
|
-
default:
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
return {
|
|
333
|
-
...run,
|
|
334
|
-
events: [...run.events, event],
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
function applyEventToLocalJob(run, event) {
|
|
338
|
-
const stepFor = (stepId) => {
|
|
339
|
-
const existing = run.steps.find((step) => step.id === stepId);
|
|
340
|
-
if (existing)
|
|
341
|
-
return existing;
|
|
342
|
-
const created = {
|
|
343
|
-
id: stepId,
|
|
344
|
-
label: stepId,
|
|
345
|
-
status: "queued",
|
|
346
|
-
};
|
|
347
|
-
run.steps.push(created);
|
|
348
|
-
return created;
|
|
349
|
-
};
|
|
350
|
-
const updateStep = (stepId, patch) => {
|
|
351
|
-
if (!stepId)
|
|
352
|
-
return;
|
|
353
|
-
Object.assign(stepFor(stepId), patch);
|
|
354
|
-
};
|
|
355
|
-
switch (event.type) {
|
|
356
|
-
case "job.started":
|
|
357
|
-
return {
|
|
358
|
-
...run,
|
|
359
|
-
status: "running",
|
|
360
|
-
started_at: run.started_at ?? event.timestamp,
|
|
361
|
-
events: [...run.events, event],
|
|
362
|
-
};
|
|
363
|
-
case "step.started":
|
|
364
|
-
updateStep(event.step_id, {
|
|
365
|
-
status: "running",
|
|
366
|
-
started_at: event.timestamp,
|
|
367
|
-
...(event.input ? { input: event.input } : {}),
|
|
368
|
-
});
|
|
369
|
-
break;
|
|
370
|
-
case "step.completed":
|
|
371
|
-
updateStep(event.step_id, {
|
|
372
|
-
status: "succeeded",
|
|
373
|
-
finished_at: event.timestamp,
|
|
374
|
-
summary: event.message ?? null,
|
|
375
|
-
...(event.output ? { output: event.output } : {}),
|
|
376
|
-
});
|
|
377
|
-
break;
|
|
378
|
-
case "step.failed":
|
|
379
|
-
updateStep(event.step_id, {
|
|
380
|
-
status: "failed",
|
|
381
|
-
finished_at: event.timestamp,
|
|
382
|
-
summary: event.message ?? null,
|
|
383
|
-
...(event.output ? { output: event.output } : {}),
|
|
384
|
-
});
|
|
385
|
-
break;
|
|
386
|
-
case "job.completed":
|
|
387
|
-
return {
|
|
388
|
-
...run,
|
|
389
|
-
status: "succeeded",
|
|
390
|
-
finished_at: run.finished_at ?? event.timestamp,
|
|
391
|
-
events: [...run.events, event],
|
|
392
|
-
};
|
|
393
|
-
case "job.failed":
|
|
394
|
-
return {
|
|
395
|
-
...run,
|
|
396
|
-
status: "failed",
|
|
397
|
-
finished_at: run.finished_at ?? event.timestamp,
|
|
398
|
-
error: event.message ?? "Job failed.",
|
|
399
|
-
events: [...run.events, event],
|
|
400
|
-
};
|
|
401
|
-
case "artifact.written":
|
|
402
|
-
case "log.appended":
|
|
403
|
-
break;
|
|
404
|
-
default:
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
return {
|
|
408
|
-
...run,
|
|
409
|
-
events: [...run.events, event],
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
function slugFromText(value) {
|
|
413
|
-
const slug = value
|
|
414
|
-
.toLowerCase()
|
|
415
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
416
|
-
.replace(/^-+|-+$/g, "")
|
|
417
|
-
.slice(0, 42)
|
|
418
|
-
.replace(/-+$/g, "");
|
|
419
|
-
return slug || "custom";
|
|
420
|
-
}
|
|
421
|
-
function escapeRegExp(value) {
|
|
422
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
423
|
-
}
|
|
424
|
-
function stringValue(values, key) {
|
|
425
|
-
const value = values?.[key];
|
|
426
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
427
|
-
}
|
|
428
|
-
function actionTypeFromValues(values) {
|
|
429
|
-
const explicit = stringValue(values, "action_type") ?? stringValue(values, "service_action");
|
|
430
|
-
if (explicit) {
|
|
431
|
-
const parsed = ActionProposalPlanActionTypeSchema.safeParse(explicit);
|
|
432
|
-
if (parsed.success && parsed.data !== "clarification")
|
|
433
|
-
return parsed.data;
|
|
434
|
-
}
|
|
435
|
-
const action = stringValue(values, "action");
|
|
436
|
-
if (action === "compile" || action === "prepare-run" || action === "run-preparation")
|
|
437
|
-
return "compile";
|
|
438
|
-
if (action === "test" || action === "check-readiness")
|
|
439
|
-
return "test";
|
|
440
|
-
if (action === "draft-readiness-checks" || action === "readiness-check-draft")
|
|
441
|
-
return "readiness-check-draft";
|
|
442
|
-
if (action === "create-method" || action === "method-authoring")
|
|
443
|
-
return "method-authoring";
|
|
444
|
-
if (action === "method-duplicate" || action === "method-remove" || action === "method-change")
|
|
445
|
-
return "method-change";
|
|
446
|
-
if (action === "preparation-remove" || action === "preparation-change")
|
|
447
|
-
return "preparation-change";
|
|
448
|
-
if (action === "improve-preparation" || action === "method-improvement")
|
|
449
|
-
return "method-improvement";
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
452
|
-
function directServiceEndpointForAction(actionType) {
|
|
453
|
-
if (actionType === "preparation-setup")
|
|
454
|
-
return "/v1/preparation-setups";
|
|
455
|
-
if (actionType === "method-change")
|
|
456
|
-
return "/v1/method-changes";
|
|
457
|
-
if (actionType === "preparation-change")
|
|
458
|
-
return "/v1/preparation-changes";
|
|
459
|
-
return null;
|
|
460
|
-
}
|
|
461
|
-
function numberValue(values, key) {
|
|
462
|
-
const value = values?.[key];
|
|
463
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
464
|
-
}
|
|
465
|
-
function testModeFromValues(values) {
|
|
466
|
-
const value = stringValue(values, "mode") ?? stringValue(values, "target");
|
|
467
|
-
if (value === "source-files")
|
|
468
|
-
return "raw";
|
|
469
|
-
if (value === "portable-context")
|
|
470
|
-
return "compiled";
|
|
471
|
-
return value === "raw" || value === "compiled" || value === "both" ? value : null;
|
|
472
|
-
}
|
|
473
|
-
function testModeValue(values, defaultMode = "both") {
|
|
474
|
-
return testModeFromValues(values) ?? defaultMode;
|
|
475
|
-
}
|
|
476
|
-
function testModeCliTarget(mode) {
|
|
477
|
-
if (mode === "raw")
|
|
478
|
-
return "source-files";
|
|
479
|
-
if (mode === "compiled")
|
|
480
|
-
return "portable-context";
|
|
481
|
-
return "both";
|
|
482
|
-
}
|
|
483
|
-
function methodIdForProposal(message, values) {
|
|
484
|
-
const explicit = stringValue(values, "method_id") ??
|
|
485
|
-
stringValue(values, "method");
|
|
486
|
-
const fromMessage = message.match(/\b(?:create|reate|eate|draft|author|build|make)\s+(?:a\s+new\s+)?(?:interf\s+)?method\s+([a-z0-9][a-z0-9-]{0,79})\b/i)?.[1];
|
|
487
|
-
return explicit ?? fromMessage ?? `custom-${slugFromText(message)}`;
|
|
488
|
-
}
|
|
489
|
-
function actionValueMethodTaskPrompt(values) {
|
|
490
|
-
const parsed = MethodAuthoringActionValuesSchema.safeParse(values);
|
|
491
|
-
return parsed.success ? methodAuthoringTaskPrompt(parsed.data) : null;
|
|
492
|
-
}
|
|
493
|
-
const METHOD_AUTHORING_INTERNAL_INSTRUCTION = /Use the attached values as the Method authoring request before proposing the action\.?/gi;
|
|
494
|
-
const METHOD_AUTHORING_LABELS = [
|
|
495
|
-
"Agent work",
|
|
496
|
-
"Portable-context output",
|
|
497
|
-
"Readiness checks",
|
|
498
|
-
"CLI preview",
|
|
499
|
-
];
|
|
500
|
-
function normalizeMethodAuthoringText(value) {
|
|
501
|
-
return value
|
|
502
|
-
.replace(/[│]+/g, " ")
|
|
503
|
-
.replace(METHOD_AUTHORING_INTERNAL_INSTRUCTION, " ")
|
|
504
|
-
.replace(/\s+/g, " ")
|
|
505
|
-
.trim();
|
|
506
|
-
}
|
|
507
|
-
function extractMethodAuthoringSection(value, label) {
|
|
508
|
-
const otherLabels = METHOD_AUTHORING_LABELS
|
|
509
|
-
.filter((candidate) => candidate !== label)
|
|
510
|
-
.map(escapeRegExp)
|
|
511
|
-
.join("|");
|
|
512
|
-
const match = value.match(new RegExp(`${escapeRegExp(label)}\\s*:\\s*([\\s\\S]*?)(?=\\s*(?:${otherLabels})\\s*:|$)`, "i"));
|
|
513
|
-
return match?.[1] ? normalizeMethodAuthoringText(match[1]) : null;
|
|
514
|
-
}
|
|
515
|
-
function stripMethodCommandPrefix(value, methodId) {
|
|
516
|
-
const specific = new RegExp(`^(?:create|reate|eate|draft|author|build|make)\\s+(?:a\\s+new\\s+)?(?:interf\\s+)?method\\s+${escapeRegExp(methodId)}\\.?\\s*`, "i");
|
|
517
|
-
const generic = /^(?:create|reate|eate|draft|author|build|make)\s+(?:a\s+new\s+)?(?:interf\s+)?method\b[^.]{0,180}\.\s*/i;
|
|
518
|
-
const stripped = normalizeMethodAuthoringText(value)
|
|
519
|
-
.replace(specific, "")
|
|
520
|
-
.replace(generic, "")
|
|
521
|
-
.trim();
|
|
522
|
-
return stripped || normalizeMethodAuthoringText(value);
|
|
523
|
-
}
|
|
524
|
-
function methodAuthoringPromptFallback(message, methodId) {
|
|
525
|
-
const cleaned = message
|
|
526
|
-
.replace(/[│]+/g, " ")
|
|
527
|
-
.replace(METHOD_AUTHORING_INTERNAL_INSTRUCTION, " ")
|
|
528
|
-
.trim();
|
|
529
|
-
const agentWork = extractMethodAuthoringSection(cleaned, "Agent work");
|
|
530
|
-
const portableOutput = extractMethodAuthoringSection(cleaned, "Portable-context output");
|
|
531
|
-
const readinessNotes = extractMethodAuthoringSection(cleaned, "Readiness checks");
|
|
532
|
-
const lines = [
|
|
533
|
-
agentWork ? `Agent work: ${stripMethodCommandPrefix(agentWork, methodId)}` : null,
|
|
534
|
-
portableOutput ? `Portable-context output: ${portableOutput}` : null,
|
|
535
|
-
readinessNotes ? `Readiness checks: ${readinessNotes}` : null,
|
|
536
|
-
].filter((line) => Boolean(line));
|
|
537
|
-
if (lines.length > 0)
|
|
538
|
-
return lines.join("\n");
|
|
539
|
-
return stripMethodCommandPrefix(cleaned, methodId);
|
|
540
|
-
}
|
|
541
|
-
function methodAuthoringHintFromPrompt(prompt) {
|
|
542
|
-
const plain = normalizeMethodAuthoringText(prompt.replace(/\b(?:Agent work|Portable-context output|Readiness checks)\s*:/gi, " "));
|
|
543
|
-
if (plain.length <= 180)
|
|
544
|
-
return plain || "Custom Method";
|
|
545
|
-
const clipped = plain.slice(0, 177).replace(/\s+\S*$/, "").trim();
|
|
546
|
-
return `${clipped || plain.slice(0, 177)}...`;
|
|
547
|
-
}
|
|
548
|
-
function methodLabelFromId(methodId) {
|
|
549
|
-
return methodId
|
|
550
|
-
.split("-")
|
|
551
|
-
.filter(Boolean)
|
|
552
|
-
.map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
|
|
553
|
-
.join(" ") || methodId;
|
|
554
|
-
}
|
|
555
|
-
function detachMethodFromPreparation(preparation, methodId) {
|
|
556
|
-
if (methodIdForSourcePreparationConfig(preparation) !== methodId)
|
|
557
|
-
return preparation;
|
|
558
|
-
const { method: _removedMethod, ...detachedPreparation } = preparation;
|
|
559
|
-
return detachedPreparation;
|
|
560
|
-
}
|
|
561
|
-
function requireSelectedMethod(preparation) {
|
|
562
|
-
const methodId = methodIdForSourcePreparationConfig(preparation);
|
|
563
|
-
if (methodId)
|
|
564
|
-
return methodId;
|
|
565
|
-
throw new Error(`Select a Method for Preparation "${preparation.name}" before preparing it.`);
|
|
566
|
-
}
|
|
567
|
-
function actionCommandPreview(actionType, preparationName, methodId, values) {
|
|
568
|
-
if (actionType === "compile") {
|
|
569
|
-
const methodSuffix = methodId ? ` # Method: ${methodId}` : "";
|
|
570
|
-
return preparationName
|
|
571
|
-
? `interf compile --preparation ${preparationName}${methodSuffix}`
|
|
572
|
-
: `interf compile${methodSuffix}`;
|
|
573
|
-
}
|
|
574
|
-
if (actionType === "test") {
|
|
575
|
-
const mode = testModeCliTarget(testModeValue(values));
|
|
576
|
-
return preparationName
|
|
577
|
-
? `interf test --preparation ${preparationName} --target ${mode}`
|
|
578
|
-
: `interf test --target ${mode}`;
|
|
579
|
-
}
|
|
580
|
-
if (actionType === "readiness-check-draft") {
|
|
581
|
-
return "interf # choose Auto-create readiness checks";
|
|
582
|
-
}
|
|
583
|
-
if (actionType === "method-authoring" || actionType === "method-improvement") {
|
|
584
|
-
return "interf create method";
|
|
585
|
-
}
|
|
586
|
-
return "Try: create a Preparation, prepare, check readiness, draft readiness checks, or draft a Method.";
|
|
587
|
-
}
|
|
588
|
-
function hasCompiledTestTarget(sourcePath, preparationConfig) {
|
|
589
|
-
const compiledPath = portableContextPath(sourcePath, preparationConfig.name);
|
|
590
|
-
if (!existsSync(compiledPath))
|
|
591
|
-
return false;
|
|
592
|
-
return createCompiledTestTarget(compiledPath, preparationConfig.name, methodIdForSourcePreparationConfig(preparationConfig) ?? DEFAULT_METHOD_ID).eligible;
|
|
593
|
-
}
|
|
594
|
-
function actionAssistantMessage(actionType, preparationName, commandPreview) {
|
|
595
|
-
const preparationSuffix = preparationName ? ` for Preparation "${preparationName}"` : "";
|
|
596
|
-
if (actionType === "compile") {
|
|
597
|
-
return `Interf prepared a prepare-run proposal${preparationSuffix}. Approve to submit it through the local Interf service and watch the run in Interf. CLI equivalent: ${commandPreview}`;
|
|
598
|
-
}
|
|
599
|
-
if (actionType === "test") {
|
|
600
|
-
return `Interf prepared a readiness-check proposal${preparationSuffix}. Approve to run the requested target against saved readiness checks. CLI equivalent: ${commandPreview}`;
|
|
601
|
-
}
|
|
602
|
-
if (actionType === "readiness-check-draft") {
|
|
603
|
-
return `Interf prepared a proposal to draft readiness checks${preparationSuffix}. Approve to ask the configured local executor to draft saved checks as a visible run. CLI equivalent: ${commandPreview}`;
|
|
604
|
-
}
|
|
605
|
-
if (actionType === "method-authoring" || actionType === "method-improvement") {
|
|
606
|
-
return `Interf prepared a Method draft proposal${preparationSuffix}. Approve to draft a reusable local Method as a visible run. CLI equivalent: ${commandPreview}`;
|
|
607
|
-
}
|
|
608
|
-
return "I could not map that to a safe Interf action. Ask for one action in plain English, such as create a Preparation, prepare the data, check readiness, draft readiness checks, or draft a Method.";
|
|
609
|
-
}
|
|
610
|
-
function passRate(passed, total) {
|
|
611
|
-
if (total <= 0)
|
|
612
|
-
return null;
|
|
613
|
-
return Math.round((passed / total) * 100);
|
|
614
|
-
}
|
|
615
|
-
function readinessTargetResult(summary, currentFingerprint, readinessRunFingerprint) {
|
|
616
|
-
if (!summary)
|
|
617
|
-
return null;
|
|
618
|
-
const resultFingerprint = readinessRunFingerprint ?? null;
|
|
619
|
-
return {
|
|
620
|
-
passed: summary.passed_cases,
|
|
621
|
-
total: summary.total_cases,
|
|
622
|
-
pass_rate: passRate(summary.passed_cases, summary.total_cases),
|
|
623
|
-
checks_fingerprint: resultFingerprint,
|
|
624
|
-
stale: Boolean(currentFingerprint && resultFingerprint && currentFingerprint !== resultFingerprint),
|
|
625
|
-
run_id: null,
|
|
626
|
-
run_path: summary.run_path,
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
function readinessSummaryForStatus(status) {
|
|
630
|
-
if (status === "ready")
|
|
631
|
-
return "Ready for agent work.";
|
|
632
|
-
if (status === "not-ready")
|
|
633
|
-
return "Readiness checks did not pass.";
|
|
634
|
-
if (status === "stale")
|
|
635
|
-
return "Readiness checks are stale for the current saved checks.";
|
|
636
|
-
if (status === "checking")
|
|
637
|
-
return "Readiness checks are running.";
|
|
638
|
-
if (status === "building")
|
|
639
|
-
return "Portable context is building.";
|
|
640
|
-
if (status === "built")
|
|
641
|
-
return "Portable context is built; readiness has not been proven yet.";
|
|
642
|
-
if (status === "not-built")
|
|
643
|
-
return "Portable context has not been built yet.";
|
|
644
|
-
if (status === "not-configured")
|
|
645
|
-
return "No readiness checks are configured.";
|
|
646
|
-
return "Latest preparation failed.";
|
|
647
|
-
}
|
|
648
|
-
function readinessStateToPreparationReadiness(readiness) {
|
|
649
|
-
return PreparationReadinessStateSchema.parse({
|
|
650
|
-
...readiness,
|
|
651
|
-
checks: readiness.checks.map((check) => ({ ...check })),
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
function buildPreparationResource(rootPath, preparation, readiness, latestCompileRunId, latestTestRunId) {
|
|
655
|
-
const methodId = methodIdForSourcePreparationConfig(preparation);
|
|
656
|
-
return PreparationResourceSchema.parse({
|
|
657
|
-
id: preparation.name,
|
|
658
|
-
name: preparation.name,
|
|
659
|
-
preparation,
|
|
660
|
-
source_path: resolveSourcePreparationPath(rootPath, preparation),
|
|
661
|
-
method_id: methodId,
|
|
662
|
-
checks: preparation.checks,
|
|
663
|
-
portable_context: {
|
|
664
|
-
preparation: preparation.name,
|
|
665
|
-
path: readiness.portable_context_path,
|
|
666
|
-
exists: readiness.portable_context_path !== null,
|
|
667
|
-
method_id: methodId,
|
|
668
|
-
latest_compile_run_id: latestCompileRunId,
|
|
669
|
-
latest_test_run_id: latestTestRunId,
|
|
670
|
-
},
|
|
671
|
-
portable_context_path: readiness.portable_context_path,
|
|
672
|
-
readiness: readinessStateToPreparationReadiness(readiness),
|
|
673
|
-
runs: {
|
|
674
|
-
latest_compile_run_id: latestCompileRunId,
|
|
675
|
-
latest_test_run_id: latestTestRunId,
|
|
676
|
-
},
|
|
677
|
-
latest_compile_run_id: latestCompileRunId,
|
|
678
|
-
latest_test_run_id: latestTestRunId,
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
function buildMethodResource(resource) {
|
|
682
|
-
return MethodResourceSchema.parse({
|
|
683
|
-
id: resource.id,
|
|
684
|
-
method_id: resource.id,
|
|
685
|
-
path: resource.path,
|
|
686
|
-
...(resource.label ? { label: resource.label } : {}),
|
|
687
|
-
...(resource.hint ? { hint: resource.hint } : {}),
|
|
688
|
-
source_kind: resource.source_kind,
|
|
689
|
-
built_in: resource.built_in,
|
|
690
|
-
active_for_preparations: resource.active_for_preparations,
|
|
691
|
-
output_paths: resource.output_paths,
|
|
692
|
-
stages: resource.stages,
|
|
693
|
-
});
|
|
694
|
-
}
|
|
30
|
+
import { MethodAuthoringActionValuesSchema, PreparationSetupActionValuesSchema, } from "./action-values.js";
|
|
31
|
+
import { compileRunToObservability, jobRunToObservability, verifyRunToObservability, uniqueArtifacts, } from "./run-observability.js";
|
|
32
|
+
/** TTL for `POST /v1/compile-runs` idempotency-key dedupe entries. */
|
|
33
|
+
const IDEMPOTENCY_TTL_MS = 60 * 60 * 1000;
|
|
34
|
+
/** Idempotency cache size at which to schedule an opportunistic prune. */
|
|
35
|
+
const IDEMPOTENCY_PRUNE_THRESHOLD = 64;
|
|
695
36
|
export class LocalServiceRuntime {
|
|
696
|
-
rootPath;
|
|
697
37
|
host;
|
|
698
38
|
port;
|
|
699
39
|
startedAt;
|
|
700
40
|
packageVersion;
|
|
701
41
|
handlers;
|
|
42
|
+
/**
|
|
43
|
+
* The seed root path the runtime was constructed with. Used as a
|
|
44
|
+
* non-preparation fallback when a preparation-independent route
|
|
45
|
+
* (methods, action proposals, runs listings) needs an anchor to load
|
|
46
|
+
* shared state (user-library methods, bundled methods, etc).
|
|
47
|
+
*/
|
|
48
|
+
rootPath;
|
|
49
|
+
/**
|
|
50
|
+
* Per-instance bearer token. Mutating routes require this on the
|
|
51
|
+
* Authorization header. `null` means token-less mode (test harness).
|
|
52
|
+
*/
|
|
53
|
+
authToken;
|
|
54
|
+
/** Map of prepDataDir -> PreparationContext. */
|
|
55
|
+
preparationContexts = new Map();
|
|
56
|
+
/** Hook called whenever a preparation is registered or deregistered. */
|
|
57
|
+
onRegistryChanged = null;
|
|
58
|
+
/** In-flight runs across all preparations. Used for `idle_for_seconds`. */
|
|
59
|
+
activeRunCount = 0;
|
|
60
|
+
/**
|
|
61
|
+
* Active compile-run cancellation handles, keyed by run id. Populated
|
|
62
|
+
* when a compile run is launched and cleared once the run reaches a
|
|
63
|
+
* terminal state. Each entry remembers where the persisted record lives
|
|
64
|
+
* so cancel can mark it without re-resolving the Preparation.
|
|
65
|
+
*/
|
|
66
|
+
activeCompileRuns = new Map();
|
|
67
|
+
/**
|
|
68
|
+
* Idempotency-key cache for `POST /v1/compile-runs`. Outer key is the
|
|
69
|
+
* resolved preparation root; inner key is the client-supplied idempotency
|
|
70
|
+
* value. Namespacing per preparation prevents key collisions across
|
|
71
|
+
* tenants on the same engine (CSO finding: a malicious preparation could
|
|
72
|
+
* otherwise hijack another preparation's run id by reusing its key).
|
|
73
|
+
* Entries expire after `IDEMPOTENCY_TTL_MS`.
|
|
74
|
+
*/
|
|
75
|
+
idempotencyKeyCache = new Map();
|
|
76
|
+
/**
|
|
77
|
+
* Read-side caches. Polling clients (Compiler UI, CLI status loops)
|
|
78
|
+
* hit list/get endpoints multiple times per second; without these,
|
|
79
|
+
* every request re-walks the filesystem and re-parses every JSON
|
|
80
|
+
* record through Zod. The runtime invalidates each cache on the
|
|
81
|
+
* matching write path. See {@link runtime-caches} for design notes.
|
|
82
|
+
*/
|
|
83
|
+
compileRunCache = new RunListingCache();
|
|
84
|
+
verifyRunCache = new RunListingCache();
|
|
85
|
+
readinessCache = new ReadinessCache();
|
|
86
|
+
sourceFilesCache = new MtimeListingCache();
|
|
87
|
+
methodListingCache = new MethodListingCache();
|
|
702
88
|
constructor(options) {
|
|
703
|
-
this.rootPath = resolve(options.rootPath);
|
|
704
89
|
this.host = options.host;
|
|
705
90
|
this.port = options.port;
|
|
706
91
|
this.startedAt = options.startedAt ?? new Date().toISOString();
|
|
707
92
|
this.packageVersion = options.packageVersion;
|
|
708
93
|
this.handlers = options.handlers ?? {};
|
|
94
|
+
this.authToken = options.authToken ?? null;
|
|
95
|
+
this.rootPath = resolve(options.rootPath);
|
|
96
|
+
// Auto-register the initial preparation so single-preparation callers
|
|
97
|
+
// (existing tests, the current `interf web` command) work without
|
|
98
|
+
// additional bootstrapping. The constructor seed is the only role
|
|
99
|
+
// `options.rootPath` plays; runtime methods take `prepDataDir`
|
|
100
|
+
// explicitly afterwards.
|
|
101
|
+
this.registerPreparation(this.rootPath);
|
|
102
|
+
}
|
|
103
|
+
setBoundPort(port) {
|
|
104
|
+
this.port = port;
|
|
105
|
+
}
|
|
106
|
+
/** Set a hook that fires whenever the registered preparations change. */
|
|
107
|
+
setOnRegistryChanged(handler) {
|
|
108
|
+
this.onRegistryChanged = handler;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Register a preparation with this runtime. Returns the PreparationContext.
|
|
112
|
+
* Idempotent: re-registering an existing preparation updates `lastActivity`.
|
|
113
|
+
*/
|
|
114
|
+
registerPreparation(prepDataDir) {
|
|
115
|
+
const resolved = resolve(prepDataDir);
|
|
116
|
+
const now = new Date().toISOString();
|
|
117
|
+
const existing = this.preparationContexts.get(resolved);
|
|
118
|
+
if (existing) {
|
|
119
|
+
existing.lastActivity = now;
|
|
120
|
+
this.onRegistryChanged?.();
|
|
121
|
+
return existing;
|
|
122
|
+
}
|
|
123
|
+
const context = {
|
|
124
|
+
rootPath: resolved,
|
|
125
|
+
startedAt: now,
|
|
126
|
+
lastActivity: now,
|
|
127
|
+
};
|
|
128
|
+
this.preparationContexts.set(resolved, context);
|
|
129
|
+
this.onRegistryChanged?.();
|
|
130
|
+
return context;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Remove a preparation from the runtime. Returns true if a preparation was
|
|
134
|
+
* removed.
|
|
135
|
+
*/
|
|
136
|
+
deregisterPreparation(prepDataDir) {
|
|
137
|
+
const resolved = resolve(prepDataDir);
|
|
138
|
+
const removed = this.preparationContexts.delete(resolved);
|
|
139
|
+
if (removed) {
|
|
140
|
+
this.onRegistryChanged?.();
|
|
141
|
+
}
|
|
142
|
+
return removed;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Most recently active preparation, or the first registered if none has
|
|
146
|
+
* activity yet. Server code uses this as the fallback when a request
|
|
147
|
+
* does not specify a preparation. Throws if none are registered.
|
|
148
|
+
*/
|
|
149
|
+
defaultPreparationDataDir() {
|
|
150
|
+
if (this.preparationContexts.size === 0) {
|
|
151
|
+
throw new Error("Local service has no registered preparations.");
|
|
152
|
+
}
|
|
153
|
+
let best = null;
|
|
154
|
+
let bestKey = -Infinity;
|
|
155
|
+
for (const context of this.preparationContexts.values()) {
|
|
156
|
+
const key = Date.parse(context.lastActivity);
|
|
157
|
+
if (Number.isFinite(key) && key > bestKey) {
|
|
158
|
+
best = context;
|
|
159
|
+
bestKey = key;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return (best ?? this.preparationContexts.values().next().value).rootPath;
|
|
163
|
+
}
|
|
164
|
+
/** Look up a preparation context by rootPath. */
|
|
165
|
+
getPreparationContext(prepDataDir) {
|
|
166
|
+
return this.preparationContexts.get(resolve(prepDataDir)) ?? null;
|
|
167
|
+
}
|
|
168
|
+
/** All registered preparations, ordered by registration time. */
|
|
169
|
+
listRegisteredPreparations() {
|
|
170
|
+
return Array.from(this.preparationContexts.values()).sort((left, right) => Date.parse(left.startedAt) - Date.parse(right.startedAt));
|
|
171
|
+
}
|
|
172
|
+
/** True when no preparations are registered. */
|
|
173
|
+
hasNoPreparations() {
|
|
174
|
+
return this.preparationContexts.size === 0;
|
|
175
|
+
}
|
|
176
|
+
/** Number of registered preparations. */
|
|
177
|
+
registeredPreparationCount() {
|
|
178
|
+
return this.preparationContexts.size;
|
|
179
|
+
}
|
|
180
|
+
/** Increment in-flight run counter. Call when a long-running run starts. */
|
|
181
|
+
beginActiveRun() {
|
|
182
|
+
this.activeRunCount += 1;
|
|
183
|
+
}
|
|
184
|
+
/** Decrement in-flight run counter. Pair with `beginActiveRun`. */
|
|
185
|
+
endActiveRun() {
|
|
186
|
+
if (this.activeRunCount > 0)
|
|
187
|
+
this.activeRunCount -= 1;
|
|
188
|
+
}
|
|
189
|
+
/** Sum of in-flight runs across all preparations. */
|
|
190
|
+
activeRuns() {
|
|
191
|
+
return this.activeRunCount;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Mark the preparation as recently active. Routes call this on entry so
|
|
195
|
+
* `idleForSeconds` and the registry snapshots stay in sync with the
|
|
196
|
+
* actual request cadence.
|
|
197
|
+
*/
|
|
198
|
+
touchPreparation(prepDataDir) {
|
|
199
|
+
const context = this.preparationContexts.get(resolve(prepDataDir));
|
|
200
|
+
if (context) {
|
|
201
|
+
context.lastActivity = new Date().toISOString();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Snapshot of registered preparations for the registry / status output.
|
|
206
|
+
* Wire shape (`ServiceRegistryWorkspace`) keeps the legacy
|
|
207
|
+
* "workspace" name for backward compatibility with the public health
|
|
208
|
+
* response and `~/.interf/services.json`. Synthetic-workspace bridge
|
|
209
|
+
* code; do not rename without coordinating an API break.
|
|
210
|
+
*/
|
|
211
|
+
registeredPreparationSnapshots() {
|
|
212
|
+
return this.listRegisteredPreparations().map((context) => ServiceRegistryWorkspaceSchema.parse({
|
|
213
|
+
control_path: context.rootPath,
|
|
214
|
+
registered_at: context.startedAt,
|
|
215
|
+
last_activity: context.lastActivity,
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
/** Seconds since the most recent preparation activity (0 if active). */
|
|
219
|
+
idleForSeconds() {
|
|
220
|
+
const all = this.listRegisteredPreparations();
|
|
221
|
+
if (all.length === 0)
|
|
222
|
+
return 0;
|
|
223
|
+
if (this.activeRunCount > 0)
|
|
224
|
+
return 0;
|
|
225
|
+
let mostRecent = 0;
|
|
226
|
+
for (const context of all) {
|
|
227
|
+
const ts = Date.parse(context.lastActivity);
|
|
228
|
+
if (Number.isFinite(ts) && ts > mostRecent)
|
|
229
|
+
mostRecent = ts;
|
|
230
|
+
}
|
|
231
|
+
if (mostRecent <= 0)
|
|
232
|
+
return 0;
|
|
233
|
+
const elapsed = Math.max(0, Date.now() - mostRecent);
|
|
234
|
+
return Math.floor(elapsed / 1000);
|
|
709
235
|
}
|
|
710
|
-
health() {
|
|
711
|
-
const sourceFolderPath = resolveConfiguredSourceFolderPath(
|
|
236
|
+
health(prepDataDir) {
|
|
237
|
+
const sourceFolderPath = prepDataDir ? resolveConfiguredSourceFolderPath(prepDataDir) : null;
|
|
712
238
|
return LocalServiceHealthSchema.parse({
|
|
713
239
|
kind: "interf-local-service-health",
|
|
714
240
|
version: 1,
|
|
@@ -716,52 +242,59 @@ export class LocalServiceRuntime {
|
|
|
716
242
|
host: this.host,
|
|
717
243
|
port: this.port,
|
|
718
244
|
service_url: buildLocalServiceUrl({ host: this.host, port: this.port }),
|
|
719
|
-
control_path:
|
|
245
|
+
...(prepDataDir ? { control_path: prepDataDir } : {}),
|
|
720
246
|
source_folder_path: sourceFolderPath,
|
|
721
247
|
started_at: this.startedAt,
|
|
722
248
|
...(this.packageVersion ? { package_version: this.packageVersion } : {}),
|
|
249
|
+
instance_started_at: this.startedAt,
|
|
250
|
+
registered_workspaces: this.registeredPreparationSnapshots(),
|
|
251
|
+
active_runs: this.activeRunCount,
|
|
252
|
+
idle_for_seconds: this.idleForSeconds(),
|
|
723
253
|
});
|
|
724
254
|
}
|
|
725
|
-
listPreparations() {
|
|
726
|
-
const config = loadSourceFolderConfig(
|
|
255
|
+
listPreparations(prepDataDir) {
|
|
256
|
+
const config = loadSourceFolderConfig(prepDataDir);
|
|
727
257
|
return listSourcePreparationConfigs(config).map((preparation) => {
|
|
728
|
-
const compileRuns = this.listCompileRunsForPreparation(preparation.name);
|
|
729
|
-
const
|
|
730
|
-
const readiness = this.computePreparationReadiness(preparation);
|
|
731
|
-
return buildPreparationResource(
|
|
258
|
+
const compileRuns = this.listCompileRunsForPreparation(prepDataDir, preparation.name);
|
|
259
|
+
const verifyRuns = this.listVerifyRunsForPreparation(prepDataDir, preparation.name);
|
|
260
|
+
const readiness = this.computePreparationReadiness(prepDataDir, preparation);
|
|
261
|
+
return buildPreparationResource(prepDataDir, preparation, readiness, compileRuns[0]?.run_id ?? null, verifyRuns[0]?.run_id ?? null);
|
|
732
262
|
});
|
|
733
263
|
}
|
|
734
|
-
getPreparation(preparationName) {
|
|
735
|
-
return this.listPreparations().find((preparation) => preparation.name === preparationName) ?? null;
|
|
264
|
+
getPreparation(prepDataDir, preparationName) {
|
|
265
|
+
return this.listPreparations(prepDataDir).find((preparation) => preparation.name === preparationName) ?? null;
|
|
736
266
|
}
|
|
737
|
-
listPreparationReadiness() {
|
|
738
|
-
return this.listReadiness().map(readinessStateToPreparationReadiness);
|
|
267
|
+
listPreparationReadiness(prepDataDir) {
|
|
268
|
+
return this.listReadiness(prepDataDir).map(readinessStateToPreparationReadiness);
|
|
739
269
|
}
|
|
740
|
-
getPreparationReadiness(preparationName) {
|
|
741
|
-
const readiness = this.getReadiness(preparationName);
|
|
270
|
+
getPreparationReadiness(prepDataDir, preparationName) {
|
|
271
|
+
const readiness = this.getReadiness(prepDataDir, preparationName);
|
|
742
272
|
return readiness ? readinessStateToPreparationReadiness(readiness) : null;
|
|
743
273
|
}
|
|
744
|
-
listReadiness() {
|
|
745
|
-
const config = loadSourceFolderConfig(
|
|
746
|
-
return listSourcePreparationConfigs(config).map((preparation) => this.computePreparationReadiness(preparation));
|
|
274
|
+
listReadiness(prepDataDir) {
|
|
275
|
+
const config = loadSourceFolderConfig(prepDataDir);
|
|
276
|
+
return listSourcePreparationConfigs(config).map((preparation) => this.computePreparationReadiness(prepDataDir, preparation));
|
|
747
277
|
}
|
|
748
|
-
getReadiness(preparationName) {
|
|
749
|
-
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(
|
|
750
|
-
return preparation ? this.computePreparationReadiness(preparation) : null;
|
|
278
|
+
getReadiness(prepDataDir, preparationName) {
|
|
279
|
+
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), preparationName);
|
|
280
|
+
return preparation ? this.computePreparationReadiness(prepDataDir, preparation) : null;
|
|
751
281
|
}
|
|
752
|
-
computePreparationReadiness(preparation) {
|
|
282
|
+
computePreparationReadiness(prepDataDir, preparation) {
|
|
283
|
+
return this.readinessCache.get(prepDataDir, preparation.name, () => this.computePreparationReadinessUncached(prepDataDir, preparation));
|
|
284
|
+
}
|
|
285
|
+
computePreparationReadinessUncached(prepDataDir, preparation) {
|
|
753
286
|
const generatedAt = new Date().toISOString();
|
|
754
|
-
const compiledPath =
|
|
287
|
+
const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
|
|
755
288
|
const contextExists = existsSync(compiledPath);
|
|
756
289
|
const compiledTarget = createCompiledTestTarget(compiledPath, preparation.name, methodIdForSourcePreparationConfig(preparation) ?? DEFAULT_METHOD_ID);
|
|
757
290
|
const contextReady = compiledTarget.eligible;
|
|
758
|
-
const compileRun = this.listCompileRunsForPreparation(preparation.name)[0] ?? null;
|
|
759
|
-
const
|
|
760
|
-
const readinessRun = this.readLatestReadinessRun(preparation.name);
|
|
291
|
+
const compileRun = this.listCompileRunsForPreparation(prepDataDir, preparation.name)[0] ?? null;
|
|
292
|
+
const verifyRun = this.listVerifyRunsForPreparation(prepDataDir, preparation.name)[0] ?? null;
|
|
293
|
+
const readinessRun = this.readLatestReadinessRun(prepDataDir, preparation.name);
|
|
761
294
|
const configuredChecks = preparation.checks.length;
|
|
762
295
|
const currentFingerprint = configuredChecks > 0 ? fingerprintReadinessChecks(preparation.checks) : null;
|
|
763
296
|
const readinessRunFingerprint = readinessRun?.checks_fingerprint ?? null;
|
|
764
|
-
const sourceResult = readinessTargetResult(readinessRun?.
|
|
297
|
+
const sourceResult = readinessTargetResult(readinessRun?.source_files, currentFingerprint, readinessRunFingerprint);
|
|
765
298
|
const contextResult = readinessTargetResult(readinessRun?.compiled, currentFingerprint, readinessRunFingerprint);
|
|
766
299
|
const checksStale = Boolean(currentFingerprint && readinessRunFingerprint && currentFingerprint !== readinessRunFingerprint);
|
|
767
300
|
const compileCheck = (() => {
|
|
@@ -843,7 +376,7 @@ export class LocalServiceRuntime {
|
|
|
843
376
|
const status = (() => {
|
|
844
377
|
if (compileRun?.status === "queued" || compileRun?.status === "running")
|
|
845
378
|
return "building";
|
|
846
|
-
if (
|
|
379
|
+
if (verifyRun?.status === "queued" || verifyRun?.status === "running")
|
|
847
380
|
return "checking";
|
|
848
381
|
if (compileRun?.status === "failed" || compileRun?.status === "cancelled")
|
|
849
382
|
return "failed";
|
|
@@ -868,7 +401,7 @@ export class LocalServiceRuntime {
|
|
|
868
401
|
summary: readinessSummaryForStatus(status),
|
|
869
402
|
portable_context_path: contextReady ? compiledPath : null,
|
|
870
403
|
latest_compile_run_id: compileRun?.run_id ?? null,
|
|
871
|
-
latest_test_run_id:
|
|
404
|
+
latest_test_run_id: verifyRun?.run_id ?? null,
|
|
872
405
|
compile: compileCheck,
|
|
873
406
|
check_results: {
|
|
874
407
|
configured: configuredChecks,
|
|
@@ -879,39 +412,52 @@ export class LocalServiceRuntime {
|
|
|
879
412
|
checks,
|
|
880
413
|
});
|
|
881
414
|
}
|
|
882
|
-
listSourceFiles(preparationName) {
|
|
883
|
-
const
|
|
415
|
+
listSourceFiles(prepDataDir, preparationName) {
|
|
416
|
+
const config = loadSourceFolderConfig(prepDataDir);
|
|
417
|
+
const preparations = listSourcePreparationConfigs(config)
|
|
884
418
|
.filter((preparation) => !preparationName || preparation.name === preparationName);
|
|
419
|
+
// 0.13+ source binding: the source folder is the user-supplied
|
|
420
|
+
// absolute path on `source_folder.path`. `preparation.path` is the
|
|
421
|
+
// portable-context name inside `prepDataDir` (legacy field; in the
|
|
422
|
+
// synthetic-workspace bridge it equals `preparation.name`). Walk
|
|
423
|
+
// the actual source bytes, not the portable-context subdir.
|
|
424
|
+
const sourceFolderPath = resolveConfiguredSourceFolderPath(prepDataDir, config) ?? prepDataDir;
|
|
885
425
|
return preparations.flatMap((preparation) => {
|
|
886
|
-
const
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
426
|
+
const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
|
|
427
|
+
// Cache by source-folder root mtime so identical UI polls do not
|
|
428
|
+
// re-walk and re-stat the entire tree. The cache imposes a short
|
|
429
|
+
// TTL (see runtime-caches.ts) so deeper changes are still picked
|
|
430
|
+
// up promptly.
|
|
431
|
+
const cacheKey = `${preparation.name}\0${sourceFolderPath}\0${compiledPath}`;
|
|
432
|
+
return this.sourceFilesCache.get(cacheKey, sourceFolderPath, () => {
|
|
433
|
+
return discoverSourceFiles(sourceFolderPath, compiledPath).sourceFiles.map((relativePath) => {
|
|
434
|
+
const absolutePath = join(sourceFolderPath, relativePath);
|
|
435
|
+
let sizeBytes = 0;
|
|
436
|
+
let modifiedAt = null;
|
|
437
|
+
try {
|
|
438
|
+
const stat = statSync(absolutePath);
|
|
439
|
+
sizeBytes = stat.size;
|
|
440
|
+
modifiedAt = stat.mtime.toISOString();
|
|
441
|
+
}
|
|
442
|
+
catch {
|
|
443
|
+
sizeBytes = 0;
|
|
444
|
+
modifiedAt = null;
|
|
445
|
+
}
|
|
446
|
+
return SourceFileResourceSchema.parse({
|
|
447
|
+
preparation: preparation.name,
|
|
448
|
+
path: relativePath,
|
|
449
|
+
absolute_path: absolutePath,
|
|
450
|
+
size_bytes: sizeBytes,
|
|
451
|
+
modified_at: modifiedAt,
|
|
452
|
+
source_folder_path: sourceFolderPath,
|
|
453
|
+
});
|
|
908
454
|
});
|
|
909
455
|
});
|
|
910
456
|
});
|
|
911
457
|
}
|
|
912
|
-
listWorkspaceFiles() {
|
|
913
|
-
const sourceFolderPath = resolveConfiguredSourceFolderPath(
|
|
914
|
-
return discoverSourceFiles(sourceFolderPath,
|
|
458
|
+
listWorkspaceFiles(prepDataDir) {
|
|
459
|
+
const sourceFolderPath = resolveConfiguredSourceFolderPath(prepDataDir) ?? prepDataDir;
|
|
460
|
+
return discoverSourceFiles(sourceFolderPath, prepDataDir).sourceFiles.map((relativePath) => {
|
|
915
461
|
const absolutePath = join(sourceFolderPath, relativePath);
|
|
916
462
|
let sizeBytes = 0;
|
|
917
463
|
let modifiedAt = null;
|
|
@@ -932,51 +478,62 @@ export class LocalServiceRuntime {
|
|
|
932
478
|
});
|
|
933
479
|
});
|
|
934
480
|
}
|
|
935
|
-
listMethods() {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
.
|
|
953
|
-
.
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
481
|
+
listMethods(prepDataDir) {
|
|
482
|
+
// The Method choices list is dominated by repeated reads of
|
|
483
|
+
// method.json + context-interface across builtin / user / workspace
|
|
484
|
+
// method roots. Key the cache off mtimes for the three roots; if
|
|
485
|
+
// any of them changes (a new local Method, an edit to the user
|
|
486
|
+
// library, etc.) the cache misses and we re-resolve.
|
|
487
|
+
const builtinRoot = join(PACKAGE_ROOT, "builtin-methods");
|
|
488
|
+
const localRoot = preparationMethodsRoot(asPreparationDataDir(prepDataDir));
|
|
489
|
+
const userRoot = userMethodsRoot();
|
|
490
|
+
return this.methodListingCache.get(prepDataDir, [builtinRoot, localRoot, userRoot], () => {
|
|
491
|
+
const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
|
|
492
|
+
const choices = listCompiledMethodChoices(prepDataDir);
|
|
493
|
+
return choices.map((method) => {
|
|
494
|
+
const activeForPreparations = preparations
|
|
495
|
+
.filter((preparation) => methodIdForSourcePreparationConfig(preparation) === method.id)
|
|
496
|
+
.map((preparation) => preparation.name);
|
|
497
|
+
return buildMethodResource({
|
|
498
|
+
id: method.id,
|
|
499
|
+
path: resolveMethodPackageSourcePath(prepDataDir, method.id) ?? method.id,
|
|
500
|
+
label: method.label,
|
|
501
|
+
hint: method.hint,
|
|
502
|
+
source_kind: method.scope === "builtin" ? "builtin" : "local",
|
|
503
|
+
built_in: method.scope === "builtin",
|
|
504
|
+
active_for_preparations: activeForPreparations,
|
|
505
|
+
output_paths: (method.contextInterface?.zones ?? [])
|
|
506
|
+
.filter((zone) => zone.role === "output")
|
|
507
|
+
.map((zone) => contextInterfaceArtifactPath(zone))
|
|
508
|
+
.sort(),
|
|
509
|
+
stages: method.stages.map((stage) => ({
|
|
510
|
+
id: stage.id,
|
|
511
|
+
label: stage.label,
|
|
512
|
+
description: stage.description,
|
|
513
|
+
contract_type: stage.contractType,
|
|
514
|
+
skill_dir: stage.skillDir,
|
|
515
|
+
role: stage.role && stage.role.trim().length > 0 ? stage.role : "general",
|
|
516
|
+
reads: stage.reads,
|
|
517
|
+
writes: stage.writes,
|
|
518
|
+
...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
|
|
519
|
+
})),
|
|
520
|
+
});
|
|
964
521
|
});
|
|
965
522
|
});
|
|
966
523
|
}
|
|
967
|
-
getMethod(methodId) {
|
|
968
|
-
return this.listMethods().find((method) => method.id === methodId) ?? null;
|
|
524
|
+
getMethod(prepDataDir, methodId) {
|
|
525
|
+
return this.listMethods(prepDataDir).find((method) => method.id === methodId) ?? null;
|
|
969
526
|
}
|
|
970
|
-
listJobs() {
|
|
971
|
-
return
|
|
527
|
+
listJobs(prepDataDir) {
|
|
528
|
+
return byCreatedAtDesc(listJsonFiles(localJobsRoot(prepDataDir))
|
|
972
529
|
.map(readLocalJobRunAt)
|
|
973
530
|
.filter((run) => run !== null));
|
|
974
531
|
}
|
|
975
|
-
getJob(runId) {
|
|
976
|
-
return this.listJobs().find((run) => run.run_id === runId) ?? null;
|
|
532
|
+
getJob(prepDataDir, runId) {
|
|
533
|
+
return this.listJobs(prepDataDir).find((run) => run.run_id === runId) ?? null;
|
|
977
534
|
}
|
|
978
|
-
getJobEvents(runId) {
|
|
979
|
-
return this.getJob(runId)?.events ?? null;
|
|
535
|
+
getJobEvents(prepDataDir, runId) {
|
|
536
|
+
return this.getJob(prepDataDir, runId)?.events ?? null;
|
|
980
537
|
}
|
|
981
538
|
getExecutorStatus() {
|
|
982
539
|
const checkedAt = new Date().toISOString();
|
|
@@ -1044,28 +601,68 @@ export class LocalServiceRuntime {
|
|
|
1044
601
|
skillsInstalled: current?.skillsInstalled ?? false,
|
|
1045
602
|
initialized: current?.initialized ?? new Date().toISOString(),
|
|
1046
603
|
});
|
|
604
|
+
// Mirror the selection into the 0.15 role-map. `setActiveAgent`
|
|
605
|
+
// also sweeps any role still pointing at the previous active so
|
|
606
|
+
// single-active-agent setups stay coherent across CLIs.
|
|
607
|
+
try {
|
|
608
|
+
setActiveAgent(selected.name);
|
|
609
|
+
}
|
|
610
|
+
catch {
|
|
611
|
+
// Best effort — the registry update is convenience for the new
|
|
612
|
+
// role-aware path. The user_config.json write above is the
|
|
613
|
+
// source of truth for the legacy `getExecutorStatus()`.
|
|
614
|
+
}
|
|
1047
615
|
return this.getExecutorStatus();
|
|
1048
616
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
617
|
+
// ─── 0.15 connected-agents primitive ─────────────────────────────
|
|
618
|
+
/**
|
|
619
|
+
* Snapshot of the merged agents registry (built-in detected + custom)
|
|
620
|
+
* with the current role-map and resolved active agent.
|
|
621
|
+
*/
|
|
622
|
+
getAgentsRegistry() {
|
|
623
|
+
const registry = loadAgentsRegistry();
|
|
624
|
+
return {
|
|
625
|
+
agents: registry.agents,
|
|
626
|
+
role_map: registry.roleMap,
|
|
627
|
+
active_agent: registry.activeAgent,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
registerCustomAgent(input) {
|
|
631
|
+
registerCustomAgent(input);
|
|
632
|
+
return this.getAgentsRegistry();
|
|
633
|
+
}
|
|
634
|
+
unregisterCustomAgent(name) {
|
|
635
|
+
unregisterCustomAgent(name);
|
|
636
|
+
return this.getAgentsRegistry();
|
|
637
|
+
}
|
|
638
|
+
patchAgentsRoleMap(patch) {
|
|
639
|
+
patchRoleMap(patch);
|
|
640
|
+
const registry = loadAgentsRegistry();
|
|
641
|
+
return {
|
|
642
|
+
role_map: registry.roleMap,
|
|
643
|
+
active_agent: registry.activeAgent,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
listActionProposals(prepDataDir) {
|
|
647
|
+
return byCreatedAtDesc(listJsonFiles(actionProposalsRoot(prepDataDir))
|
|
1051
648
|
.map(readActionProposalAt)
|
|
1052
649
|
.filter((proposal) => proposal !== null));
|
|
1053
650
|
}
|
|
1054
|
-
getActionProposal(proposalId) {
|
|
1055
|
-
return this.listActionProposals().find((proposal) => proposal.proposal_id === proposalId) ?? null;
|
|
651
|
+
getActionProposal(prepDataDir, proposalId) {
|
|
652
|
+
return this.listActionProposals(prepDataDir).find((proposal) => proposal.proposal_id === proposalId) ?? null;
|
|
1056
653
|
}
|
|
1057
|
-
async createActionProposal(requestValue) {
|
|
654
|
+
async createActionProposal(prepDataDir, requestValue) {
|
|
1058
655
|
const request = ActionProposalCreateRequestSchema.parse(requestValue);
|
|
1059
656
|
const proposal = ActionProposalResourceSchema.parse({
|
|
1060
|
-
...(await this.buildActionProposal(request)),
|
|
657
|
+
...(await this.buildActionProposal(prepDataDir, request)),
|
|
1061
658
|
client_origin: request.client_origin,
|
|
1062
659
|
});
|
|
1063
|
-
this.writeActionProposal(proposal);
|
|
660
|
+
this.writeActionProposal(prepDataDir, proposal);
|
|
1064
661
|
return proposal;
|
|
1065
662
|
}
|
|
1066
|
-
async decideActionProposal(proposalId, requestValue) {
|
|
663
|
+
async decideActionProposal(prepDataDir, proposalId, requestValue) {
|
|
1067
664
|
const decision = ActionProposalApprovalRequestSchema.parse(requestValue);
|
|
1068
|
-
const current = this.getActionProposal(proposalId);
|
|
665
|
+
const current = this.getActionProposal(prepDataDir, proposalId);
|
|
1069
666
|
if (!current)
|
|
1070
667
|
return null;
|
|
1071
668
|
if (current.status !== "awaiting_approval") {
|
|
@@ -1082,11 +679,11 @@ export class LocalServiceRuntime {
|
|
|
1082
679
|
...(decision.note ? { note: decision.note } : {}),
|
|
1083
680
|
},
|
|
1084
681
|
});
|
|
1085
|
-
this.writeActionProposal(decided);
|
|
682
|
+
this.writeActionProposal(prepDataDir, decided);
|
|
1086
683
|
if (!decision.approved)
|
|
1087
684
|
return decided;
|
|
1088
685
|
try {
|
|
1089
|
-
const submission = await this.submitActionProposal(decided);
|
|
686
|
+
const submission = await this.submitActionProposal(prepDataDir, decided);
|
|
1090
687
|
const submitted = ActionProposalResourceSchema.parse({
|
|
1091
688
|
...decided,
|
|
1092
689
|
status: "submitted",
|
|
@@ -1094,7 +691,7 @@ export class LocalServiceRuntime {
|
|
|
1094
691
|
submitted_run_id: submission.runId,
|
|
1095
692
|
submitted_run_type: submission.runType,
|
|
1096
693
|
});
|
|
1097
|
-
this.writeActionProposal(submitted);
|
|
694
|
+
this.writeActionProposal(prepDataDir, submitted);
|
|
1098
695
|
return submitted;
|
|
1099
696
|
}
|
|
1100
697
|
catch (error) {
|
|
@@ -1104,25 +701,39 @@ export class LocalServiceRuntime {
|
|
|
1104
701
|
updated_at: new Date().toISOString(),
|
|
1105
702
|
error: error instanceof Error ? error.message : String(error),
|
|
1106
703
|
});
|
|
1107
|
-
this.writeActionProposal(failed);
|
|
704
|
+
this.writeActionProposal(prepDataDir, failed);
|
|
1108
705
|
return failed;
|
|
1109
706
|
}
|
|
1110
707
|
}
|
|
1111
|
-
listRunObservability() {
|
|
708
|
+
listRunObservability(prepDataDir) {
|
|
1112
709
|
return [
|
|
1113
|
-
...this.listCompileRuns().map((resource) => compileRunToObservability(resource.run)),
|
|
1114
|
-
...this.
|
|
1115
|
-
...this.listJobs().map(jobRunToObservability),
|
|
710
|
+
...this.listCompileRuns(prepDataDir).map((resource) => compileRunToObservability(resource.run)),
|
|
711
|
+
...this.listVerifyRuns(prepDataDir).map(verifyRunToObservability),
|
|
712
|
+
...this.listJobs(prepDataDir).map(jobRunToObservability),
|
|
1116
713
|
].sort((left, right) => {
|
|
1117
714
|
const leftTime = timestampKey(left.started_at ?? left.created_at ?? left.finished_at);
|
|
1118
715
|
const rightTime = timestampKey(right.started_at ?? right.created_at ?? right.finished_at);
|
|
1119
716
|
return rightTime - leftTime;
|
|
1120
717
|
});
|
|
1121
718
|
}
|
|
1122
|
-
getRunObservability(runId) {
|
|
1123
|
-
return this.listRunObservability().find((run) => run.run_id === runId) ?? null;
|
|
719
|
+
getRunObservability(prepDataDir, runId) {
|
|
720
|
+
return this.listRunObservability(prepDataDir).find((run) => run.run_id === runId) ?? null;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Method-scoped runs: every method-authoring or method-improvement job
|
|
724
|
+
* whose `method` matches `methodId`. Surfaced through
|
|
725
|
+
* `GET /v1/methods/<id>/runs` so Method Detail can show the full audit
|
|
726
|
+
* trail of authoring + improvement work for a Method.
|
|
727
|
+
*/
|
|
728
|
+
listMethodRuns(prepDataDir, methodId) {
|
|
729
|
+
return this.listRunObservability(prepDataDir).filter((run) => {
|
|
730
|
+
if (run.method !== methodId)
|
|
731
|
+
return false;
|
|
732
|
+
return (run.run_type === "method-authoring" ||
|
|
733
|
+
run.run_type === "method-improvement");
|
|
734
|
+
});
|
|
1124
735
|
}
|
|
1125
|
-
createJobRun(requestValue) {
|
|
736
|
+
createJobRun(prepDataDir, requestValue) {
|
|
1126
737
|
const request = LocalJobRunCreateRequestSchema.parse(requestValue);
|
|
1127
738
|
const runId = createRunId("job");
|
|
1128
739
|
const now = new Date().toISOString();
|
|
@@ -1154,12 +765,12 @@ export class LocalServiceRuntime {
|
|
|
1154
765
|
},
|
|
1155
766
|
],
|
|
1156
767
|
});
|
|
1157
|
-
this.writeJobRun(run);
|
|
768
|
+
this.writeJobRun(prepDataDir, run);
|
|
1158
769
|
return run;
|
|
1159
770
|
}
|
|
1160
|
-
appendJobRunEvent(runId, requestValue) {
|
|
771
|
+
appendJobRunEvent(prepDataDir, runId, requestValue) {
|
|
1161
772
|
const request = LocalJobEventAppendRequestSchema.parse(requestValue);
|
|
1162
|
-
const current = this.getJob(runId);
|
|
773
|
+
const current = this.getJob(prepDataDir, runId);
|
|
1163
774
|
if (!current)
|
|
1164
775
|
return null;
|
|
1165
776
|
const event = {
|
|
@@ -1174,12 +785,12 @@ export class LocalServiceRuntime {
|
|
|
1174
785
|
...(request.output ? { output: request.output } : {}),
|
|
1175
786
|
};
|
|
1176
787
|
const next = LocalJobRunResourceSchema.parse(applyEventToLocalJob(current, event));
|
|
1177
|
-
this.writeJobRun(next);
|
|
788
|
+
this.writeJobRun(prepDataDir, next);
|
|
1178
789
|
return next;
|
|
1179
790
|
}
|
|
1180
|
-
async createReadinessCheckDraftRun(requestValue) {
|
|
791
|
+
async createReadinessCheckDraftRun(prepDataDir, requestValue) {
|
|
1181
792
|
const request = ReadinessCheckDraftCreateRequestSchema.parse(requestValue);
|
|
1182
|
-
const job = this.createJobRun({
|
|
793
|
+
const job = this.createJobRun(prepDataDir, {
|
|
1183
794
|
job_type: "readiness-check-draft",
|
|
1184
795
|
title: `Draft readiness checks for ${request.preparation}`,
|
|
1185
796
|
preparation: request.preparation,
|
|
@@ -1207,7 +818,7 @@ export class LocalServiceRuntime {
|
|
|
1207
818
|
},
|
|
1208
819
|
],
|
|
1209
820
|
});
|
|
1210
|
-
this.appendJobRunEvent(job.run_id, {
|
|
821
|
+
this.appendJobRunEvent(prepDataDir, job.run_id, {
|
|
1211
822
|
type: "step.started",
|
|
1212
823
|
step_id: "read-source",
|
|
1213
824
|
message: "Reading source files for readiness-check evidence.",
|
|
@@ -1216,7 +827,7 @@ export class LocalServiceRuntime {
|
|
|
1216
827
|
source_folder_path: request.source_folder_path,
|
|
1217
828
|
},
|
|
1218
829
|
});
|
|
1219
|
-
this.appendJobRunEvent(job.run_id, {
|
|
830
|
+
this.appendJobRunEvent(prepDataDir, job.run_id, {
|
|
1220
831
|
type: "step.completed",
|
|
1221
832
|
step_id: "read-source",
|
|
1222
833
|
message: "Source folder is ready for drafting readiness checks.",
|
|
@@ -1225,7 +836,7 @@ export class LocalServiceRuntime {
|
|
|
1225
836
|
source_folder_path: request.source_folder_path,
|
|
1226
837
|
},
|
|
1227
838
|
});
|
|
1228
|
-
this.appendJobRunEvent(job.run_id, {
|
|
839
|
+
this.appendJobRunEvent(prepDataDir, job.run_id, {
|
|
1229
840
|
type: "step.started",
|
|
1230
841
|
step_id: "agent-draft",
|
|
1231
842
|
message: "Drafting saved readiness checks from the source files.",
|
|
@@ -1234,30 +845,31 @@ export class LocalServiceRuntime {
|
|
|
1234
845
|
target_count: request.target_count,
|
|
1235
846
|
},
|
|
1236
847
|
});
|
|
1237
|
-
void this.runReadinessCheckDraftInBackground(request, job.run_id);
|
|
1238
|
-
return this.getJob(job.run_id) ?? job;
|
|
848
|
+
void this.runReadinessCheckDraftInBackground(prepDataDir, request, job.run_id);
|
|
849
|
+
return this.getJob(prepDataDir, job.run_id) ?? job;
|
|
1239
850
|
}
|
|
1240
|
-
applyMethodChange(requestValue) {
|
|
851
|
+
applyMethodChange(prepDataDir, requestValue) {
|
|
1241
852
|
const request = MethodChangeCreateRequestSchema.parse(requestValue);
|
|
1242
853
|
const outputPath = request.operation === "duplicate"
|
|
1243
|
-
? methodDefinitionPath(
|
|
1244
|
-
: methodDefinitionPath(
|
|
854
|
+
? methodDefinitionPath(prepDataDir, request.new_method_id)
|
|
855
|
+
: methodDefinitionPath(prepDataDir, request.method);
|
|
1245
856
|
if (request.operation === "duplicate") {
|
|
1246
|
-
if (resolveMethodPackageSourcePath(
|
|
857
|
+
if (resolveMethodPackageSourcePath(prepDataDir, request.new_method_id)) {
|
|
1247
858
|
throw new Error(`Method "${request.new_method_id}" already exists.`);
|
|
1248
859
|
}
|
|
1249
|
-
if (!resolveMethodPackageSourcePath(
|
|
860
|
+
if (!resolveMethodPackageSourcePath(prepDataDir, request.method)) {
|
|
1250
861
|
throw new Error(`Method "${request.method}" does not exist.`);
|
|
1251
862
|
}
|
|
1252
863
|
const label = request.label ?? methodLabelFromId(request.new_method_id);
|
|
1253
864
|
const hint = request.hint ?? `Duplicate of ${request.method}`;
|
|
1254
865
|
const methodPath = seedLocalMethodPackageFromBase({
|
|
1255
|
-
|
|
866
|
+
prepDataDir,
|
|
1256
867
|
baseMethodId: request.method,
|
|
1257
868
|
methodId: request.new_method_id,
|
|
1258
869
|
label,
|
|
1259
870
|
hint,
|
|
1260
871
|
});
|
|
872
|
+
this.methodListingCache.invalidate(prepDataDir);
|
|
1261
873
|
return MethodChangeResultSchema.parse({
|
|
1262
874
|
kind: "interf-method-change-result",
|
|
1263
875
|
version: 1,
|
|
@@ -1273,19 +885,24 @@ export class LocalServiceRuntime {
|
|
|
1273
885
|
if (request.confirmation !== request.method) {
|
|
1274
886
|
throw new Error(`Type ${request.method} to confirm Method removal.`);
|
|
1275
887
|
}
|
|
1276
|
-
const localMethodPath = methodDefinitionPath(
|
|
888
|
+
const localMethodPath = methodDefinitionPath(prepDataDir, request.method);
|
|
1277
889
|
if (request.method === DEFAULT_METHOD_ID || !existsSync(localMethodPath)) {
|
|
1278
890
|
throw new Error(`Method "${request.method}" is not a removable local Method.`);
|
|
1279
891
|
}
|
|
1280
|
-
const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(
|
|
892
|
+
const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
|
|
1281
893
|
const updatedPreparations = preparations
|
|
1282
894
|
.filter((preparation) => methodIdForSourcePreparationConfig(preparation) === request.method);
|
|
1283
895
|
if (updatedPreparations.length > 0) {
|
|
1284
|
-
saveSourceFolderConfig(
|
|
896
|
+
saveSourceFolderConfig(prepDataDir, {
|
|
1285
897
|
preparations: preparations.map((preparation) => detachMethodFromPreparation(preparation, request.method)),
|
|
1286
898
|
});
|
|
899
|
+
// Detaching a Method changes readiness shape for those Preparations.
|
|
900
|
+
for (const preparation of updatedPreparations) {
|
|
901
|
+
this.readinessCache.invalidatePreparation(prepDataDir, preparation.name);
|
|
902
|
+
}
|
|
1287
903
|
}
|
|
1288
904
|
rmSync(outputPath, { recursive: true, force: true });
|
|
905
|
+
this.methodListingCache.invalidate(prepDataDir);
|
|
1289
906
|
return MethodChangeResultSchema.parse({
|
|
1290
907
|
kind: "interf-method-change-result",
|
|
1291
908
|
version: 1,
|
|
@@ -1299,7 +916,7 @@ export class LocalServiceRuntime {
|
|
|
1299
916
|
: `Removed Method ${request.method}.`,
|
|
1300
917
|
});
|
|
1301
918
|
}
|
|
1302
|
-
applyPreparationSetup(requestValue) {
|
|
919
|
+
applyPreparationSetup(prepDataDir, requestValue) {
|
|
1303
920
|
const request = PreparationSetupCreateRequestSchema.parse(requestValue);
|
|
1304
921
|
const preparationConfig = request.preparation;
|
|
1305
922
|
const methodId = methodIdForSourcePreparationConfig(preparationConfig) ?? DEFAULT_METHOD_ID;
|
|
@@ -1307,11 +924,18 @@ export class LocalServiceRuntime {
|
|
|
1307
924
|
...preparationConfig,
|
|
1308
925
|
method: methodId,
|
|
1309
926
|
};
|
|
1310
|
-
const sourceFolderPath = resolveSourcePreparationPath(
|
|
927
|
+
const sourceFolderPath = resolveSourcePreparationPath(prepDataDir, normalizedPreparationConfig);
|
|
1311
928
|
if (!existsSync(sourceFolderPath) || !statSync(sourceFolderPath).isDirectory()) {
|
|
1312
929
|
throw new Error(`Source folder "${preparationConfig.path}" is not available.`);
|
|
1313
930
|
}
|
|
1314
|
-
upsertSourcePreparationConfig(
|
|
931
|
+
upsertSourcePreparationConfig(prepDataDir, normalizedPreparationConfig);
|
|
932
|
+
// The Preparation's bound source folder + Method may have changed:
|
|
933
|
+
// bust the per-preparation readiness, runs, and method-listing
|
|
934
|
+
// caches so the next read reflects the new shape.
|
|
935
|
+
this.readinessCache.invalidatePreparation(prepDataDir, normalizedPreparationConfig.name);
|
|
936
|
+
this.compileRunCache.invalidatePreparation(prepDataDir, normalizedPreparationConfig.name);
|
|
937
|
+
this.verifyRunCache.invalidatePreparation(prepDataDir, normalizedPreparationConfig.name);
|
|
938
|
+
this.methodListingCache.invalidate(prepDataDir);
|
|
1315
939
|
const operation = request.setup_mode === "select-method" ? "select-method" : "create";
|
|
1316
940
|
return PreparationSetupResultSchema.parse({
|
|
1317
941
|
kind: "interf-preparation-setup-result",
|
|
@@ -1320,47 +944,55 @@ export class LocalServiceRuntime {
|
|
|
1320
944
|
preparation: normalizedPreparationConfig.name,
|
|
1321
945
|
method: methodId,
|
|
1322
946
|
source_folder_path: sourceFolderPath,
|
|
1323
|
-
config_path:
|
|
1324
|
-
portable_context_path:
|
|
947
|
+
config_path: preparationConfigPath(asPreparationDataDir(prepDataDir)),
|
|
948
|
+
portable_context_path: preparationPortableContextPath(asPreparationDataDir(prepDataDir), normalizedPreparationConfig.name),
|
|
1325
949
|
changed: true,
|
|
1326
950
|
message: operation === "select-method"
|
|
1327
951
|
? `Preparation ${normalizedPreparationConfig.name} now uses Method ${methodId}.`
|
|
1328
952
|
: `Preparation ${normalizedPreparationConfig.name} is saved.`,
|
|
1329
953
|
});
|
|
1330
954
|
}
|
|
1331
|
-
applyPreparationChange(requestValue) {
|
|
955
|
+
applyPreparationChange(prepDataDir, requestValue) {
|
|
1332
956
|
const request = PreparationChangeCreateRequestSchema.parse(requestValue);
|
|
1333
957
|
if (request.confirmation !== request.preparation) {
|
|
1334
958
|
throw new Error(`Type ${request.preparation} to confirm Preparation removal.`);
|
|
1335
959
|
}
|
|
1336
|
-
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(
|
|
960
|
+
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), request.preparation);
|
|
1337
961
|
if (!preparation) {
|
|
1338
962
|
throw new Error(`Preparation "${request.preparation}" is not saved.`);
|
|
1339
963
|
}
|
|
1340
|
-
removeSourcePreparationConfig(
|
|
964
|
+
removeSourcePreparationConfig(prepDataDir, request.preparation);
|
|
965
|
+
this.readinessCache.invalidatePreparation(prepDataDir, request.preparation);
|
|
966
|
+
this.compileRunCache.invalidatePreparation(prepDataDir, request.preparation);
|
|
967
|
+
this.verifyRunCache.invalidatePreparation(prepDataDir, request.preparation);
|
|
968
|
+
this.methodListingCache.invalidate(prepDataDir);
|
|
1341
969
|
return PreparationChangeResultSchema.parse({
|
|
1342
970
|
kind: "interf-preparation-change-result",
|
|
1343
971
|
version: 1,
|
|
1344
972
|
operation: "remove",
|
|
1345
973
|
preparation: request.preparation,
|
|
1346
|
-
config_path:
|
|
1347
|
-
portable_context_path:
|
|
974
|
+
config_path: preparationConfigPath(asPreparationDataDir(prepDataDir)),
|
|
975
|
+
portable_context_path: preparationPortableContextPath(asPreparationDataDir(prepDataDir), request.preparation),
|
|
1348
976
|
portable_context_retained: true,
|
|
1349
977
|
changed: true,
|
|
1350
978
|
message: `Removed Preparation ${request.preparation}. Portable Context files were retained.`,
|
|
1351
979
|
});
|
|
1352
980
|
}
|
|
1353
|
-
applyReset(requestValue) {
|
|
981
|
+
applyReset(prepDataDir, requestValue) {
|
|
1354
982
|
const request = ResetRequestSchema.parse(requestValue);
|
|
1355
|
-
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(
|
|
983
|
+
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), request.preparation);
|
|
1356
984
|
if (!preparation) {
|
|
1357
985
|
throw new Error(`Preparation "${request.preparation}" is not saved.`);
|
|
1358
986
|
}
|
|
1359
|
-
const compiledPath =
|
|
987
|
+
const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), request.preparation);
|
|
1360
988
|
if (!existsSync(compiledPath)) {
|
|
1361
989
|
throw new Error(`Portable Context for Preparation "${request.preparation}" does not exist.`);
|
|
1362
990
|
}
|
|
1363
991
|
resetCompiledGeneratedState(compiledPath, request.scope);
|
|
992
|
+
// Reset wipes generated state, including saved compile/test/readiness records.
|
|
993
|
+
this.compileRunCache.invalidatePreparation(prepDataDir, request.preparation);
|
|
994
|
+
this.verifyRunCache.invalidatePreparation(prepDataDir, request.preparation);
|
|
995
|
+
this.readinessCache.invalidatePreparation(prepDataDir, request.preparation);
|
|
1364
996
|
return ResetResultSchema.parse({
|
|
1365
997
|
kind: "interf-reset-result",
|
|
1366
998
|
version: 1,
|
|
@@ -1371,16 +1003,16 @@ export class LocalServiceRuntime {
|
|
|
1371
1003
|
message: `Reset ${request.scope} state for Preparation ${request.preparation}.`,
|
|
1372
1004
|
});
|
|
1373
1005
|
}
|
|
1374
|
-
async createMethodAuthoringRun(requestValue, jobType = "method-authoring") {
|
|
1006
|
+
async createMethodAuthoringRun(prepDataDir, requestValue, jobType = "method-authoring") {
|
|
1375
1007
|
const request = MethodAuthoringCreateRequestSchema.parse(requestValue);
|
|
1376
1008
|
const isImprovement = jobType === "method-improvement";
|
|
1377
|
-
const job = this.createJobRun({
|
|
1009
|
+
const job = this.createJobRun(prepDataDir, {
|
|
1378
1010
|
job_type: jobType,
|
|
1379
1011
|
title: isImprovement ? `Improve Method ${request.method_id}` : `Draft Method ${request.method_id}`,
|
|
1380
1012
|
preparation: request.preparation ?? null,
|
|
1381
1013
|
method: request.method_id,
|
|
1382
1014
|
source_path: request.source_folder_path,
|
|
1383
|
-
output_path:
|
|
1015
|
+
output_path: preparationMethodPackagePath(asPreparationDataDir(prepDataDir), request.method_id),
|
|
1384
1016
|
steps: [
|
|
1385
1017
|
{
|
|
1386
1018
|
id: "inspect-source",
|
|
@@ -1409,7 +1041,7 @@ export class LocalServiceRuntime {
|
|
|
1409
1041
|
},
|
|
1410
1042
|
],
|
|
1411
1043
|
});
|
|
1412
|
-
this.appendJobRunEvent(job.run_id, {
|
|
1044
|
+
this.appendJobRunEvent(prepDataDir, job.run_id, {
|
|
1413
1045
|
type: "step.started",
|
|
1414
1046
|
step_id: "inspect-source",
|
|
1415
1047
|
message: isImprovement ? "Inspecting source files for Method improvement." : "Inspecting source files for Method drafting.",
|
|
@@ -1419,7 +1051,7 @@ export class LocalServiceRuntime {
|
|
|
1419
1051
|
checks: request.checks.length,
|
|
1420
1052
|
},
|
|
1421
1053
|
});
|
|
1422
|
-
this.appendJobRunEvent(job.run_id, {
|
|
1054
|
+
this.appendJobRunEvent(prepDataDir, job.run_id, {
|
|
1423
1055
|
type: "step.completed",
|
|
1424
1056
|
step_id: "inspect-source",
|
|
1425
1057
|
message: "Source context is ready.",
|
|
@@ -1428,7 +1060,7 @@ export class LocalServiceRuntime {
|
|
|
1428
1060
|
checks: request.checks.length,
|
|
1429
1061
|
},
|
|
1430
1062
|
});
|
|
1431
|
-
this.appendJobRunEvent(job.run_id, {
|
|
1063
|
+
this.appendJobRunEvent(prepDataDir, job.run_id, {
|
|
1432
1064
|
type: "step.started",
|
|
1433
1065
|
step_id: "draft-package",
|
|
1434
1066
|
message: isImprovement ? "Improving Method package." : "Drafting Method package.",
|
|
@@ -1438,23 +1070,23 @@ export class LocalServiceRuntime {
|
|
|
1438
1070
|
task_prompt: request.task_prompt,
|
|
1439
1071
|
},
|
|
1440
1072
|
});
|
|
1441
|
-
void this.runMethodAuthoringInBackground(request, job.run_id);
|
|
1442
|
-
return this.getJob(job.run_id) ?? job;
|
|
1073
|
+
void this.runMethodAuthoringInBackground(prepDataDir, request, job.run_id);
|
|
1074
|
+
return this.getJob(prepDataDir, job.run_id) ?? job;
|
|
1443
1075
|
}
|
|
1444
|
-
listPortableContexts() {
|
|
1445
|
-
return listSourcePreparationConfigs(loadSourceFolderConfig(
|
|
1446
|
-
.map((preparation) => this.getPortableContext(preparation.name))
|
|
1076
|
+
listPortableContexts(prepDataDir) {
|
|
1077
|
+
return listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))
|
|
1078
|
+
.map((preparation) => this.getPortableContext(prepDataDir, preparation.name))
|
|
1447
1079
|
.filter((context) => context !== null);
|
|
1448
1080
|
}
|
|
1449
|
-
getPortableContext(preparationName) {
|
|
1450
|
-
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(
|
|
1081
|
+
getPortableContext(prepDataDir, preparationName) {
|
|
1082
|
+
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), preparationName);
|
|
1451
1083
|
if (!preparation)
|
|
1452
1084
|
return null;
|
|
1453
|
-
const path =
|
|
1085
|
+
const path = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparation.name);
|
|
1454
1086
|
const config = readInterfConfig(path);
|
|
1455
|
-
const compileRuns = this.listCompileRunsForPreparation(preparation.name);
|
|
1456
|
-
const
|
|
1457
|
-
const readiness = this.computePreparationReadiness(preparation);
|
|
1087
|
+
const compileRuns = this.listCompileRunsForPreparation(prepDataDir, preparation.name);
|
|
1088
|
+
const verifyRuns = this.listVerifyRunsForPreparation(prepDataDir, preparation.name);
|
|
1089
|
+
const readiness = this.computePreparationReadiness(prepDataDir, preparation);
|
|
1458
1090
|
const method = config?.method ?? methodIdForSourcePreparationConfig(preparation);
|
|
1459
1091
|
return PortableContextResourceSchema.parse({
|
|
1460
1092
|
preparation: preparation.name,
|
|
@@ -1463,56 +1095,69 @@ export class LocalServiceRuntime {
|
|
|
1463
1095
|
readiness,
|
|
1464
1096
|
method,
|
|
1465
1097
|
latest_compile_run_id: compileRuns[0]?.run_id ?? null,
|
|
1466
|
-
latest_test_run_id:
|
|
1098
|
+
latest_test_run_id: verifyRuns[0]?.run_id ?? null,
|
|
1467
1099
|
artifacts: uniqueArtifacts(compileRuns[0]?.stages.flatMap((stage) => stage.artifacts) ?? []),
|
|
1468
1100
|
});
|
|
1469
1101
|
}
|
|
1470
|
-
listCompileRuns() {
|
|
1471
|
-
return
|
|
1472
|
-
.flatMap((preparation) => this.listCompileRunsForPreparation(preparation.name))).map((run) => CompileRunResourceSchema.parse({ run }));
|
|
1473
|
-
}
|
|
1474
|
-
listCompileRunsForPreparation(preparationName) {
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1102
|
+
listCompileRuns(prepDataDir) {
|
|
1103
|
+
return byCreatedAtDesc(listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))
|
|
1104
|
+
.flatMap((preparation) => this.listCompileRunsForPreparation(prepDataDir, preparation.name))).map((run) => CompileRunResourceSchema.parse({ run }));
|
|
1105
|
+
}
|
|
1106
|
+
listCompileRunsForPreparation(prepDataDir, preparationName) {
|
|
1107
|
+
return this.compileRunCache.get(prepDataDir, preparationName, () => {
|
|
1108
|
+
const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparationName);
|
|
1109
|
+
return byCreatedAtDesc(listJsonFiles(compileRunsRoot(compiledPath))
|
|
1110
|
+
.map(readCompileRunAt)
|
|
1111
|
+
.filter((run) => run !== null));
|
|
1112
|
+
}, (run) => run.run_id);
|
|
1113
|
+
}
|
|
1114
|
+
getCompileRun(prepDataDir, runId) {
|
|
1115
|
+
// Fast path: if the runId was seen during a recent listing, look up
|
|
1116
|
+
// its owning preparation directly and return that preparation's
|
|
1117
|
+
// cached entry instead of scanning every preparation on disk.
|
|
1118
|
+
const known = this.compileRunCache.preparationFor(prepDataDir, runId);
|
|
1119
|
+
if (known) {
|
|
1120
|
+
const found = this.listCompileRunsForPreparation(prepDataDir, known).find((entry) => entry.run_id === runId);
|
|
1121
|
+
if (found)
|
|
1122
|
+
return CompileRunResourceSchema.parse({ run: found });
|
|
1123
|
+
}
|
|
1124
|
+
// Slow path: scan all preparations. Falls through after a cache
|
|
1125
|
+
// miss for an in-flight run created before this process restarted.
|
|
1126
|
+
for (const resource of this.listCompileRuns(prepDataDir)) {
|
|
1482
1127
|
if (resource.run.run_id === runId)
|
|
1483
1128
|
return resource;
|
|
1484
1129
|
}
|
|
1485
1130
|
return null;
|
|
1486
1131
|
}
|
|
1487
|
-
getCompileRunEvents(runId) {
|
|
1488
|
-
return this.getCompileRun(runId)?.run.events ?? null;
|
|
1132
|
+
getCompileRunEvents(prepDataDir, runId) {
|
|
1133
|
+
return this.getCompileRun(prepDataDir, runId)?.run.events ?? null;
|
|
1489
1134
|
}
|
|
1490
|
-
getCompileRunProof(runId) {
|
|
1491
|
-
const run = this.getCompileRun(runId)?.run;
|
|
1135
|
+
getCompileRunProof(prepDataDir, runId) {
|
|
1136
|
+
const run = this.getCompileRun(prepDataDir, runId)?.run;
|
|
1492
1137
|
if (!run)
|
|
1493
1138
|
return null;
|
|
1494
1139
|
return run.stages
|
|
1495
1140
|
.map((stage) => stage.latest_proof ?? null)
|
|
1496
1141
|
.filter((proof) => proof !== null);
|
|
1497
1142
|
}
|
|
1498
|
-
getCompileRunArtifacts(runId) {
|
|
1499
|
-
const run = this.getCompileRun(runId)?.run;
|
|
1143
|
+
getCompileRunArtifacts(prepDataDir, runId) {
|
|
1144
|
+
const run = this.getCompileRun(prepDataDir, runId)?.run;
|
|
1500
1145
|
if (!run)
|
|
1501
1146
|
return null;
|
|
1502
1147
|
return uniqueArtifacts(run.stages.flatMap((stage) => stage.artifacts));
|
|
1503
1148
|
}
|
|
1504
|
-
async createCompileRun(requestValue) {
|
|
1149
|
+
async createCompileRun(prepDataDir, requestValue) {
|
|
1505
1150
|
const request = CompileRunCreateRequestSchema.parse(requestValue);
|
|
1506
|
-
const preparationConfig = this.resolvePreparationConfig(request.preparation, {
|
|
1151
|
+
const preparationConfig = this.resolvePreparationConfig(prepDataDir, request.preparation, {
|
|
1507
1152
|
method: request.method,
|
|
1508
1153
|
max_attempts: request.max_attempts,
|
|
1509
1154
|
max_loops: request.max_loops,
|
|
1510
1155
|
});
|
|
1511
|
-
const compiledPath = this.ensureCompiledForRun(preparationConfig);
|
|
1156
|
+
const compiledPath = this.ensureCompiledForRun(prepDataDir, preparationConfig);
|
|
1512
1157
|
const runId = createRunId("compile");
|
|
1513
1158
|
const now = new Date().toISOString();
|
|
1514
1159
|
const method = getCompiledMethod(requireSelectedMethod(preparationConfig), {
|
|
1515
|
-
|
|
1160
|
+
prepDataDir,
|
|
1516
1161
|
});
|
|
1517
1162
|
const stageTotal = method.stages.length;
|
|
1518
1163
|
const run = CompileRunSchema.parse({
|
|
@@ -1523,7 +1168,7 @@ export class LocalServiceRuntime {
|
|
|
1523
1168
|
preparation: preparationConfig.name,
|
|
1524
1169
|
method: method.id,
|
|
1525
1170
|
backend: "native",
|
|
1526
|
-
source_path: resolveSourcePreparationPath(
|
|
1171
|
+
source_path: resolveSourcePreparationPath(prepDataDir, preparationConfig),
|
|
1527
1172
|
portable_context_path: compiledPath,
|
|
1528
1173
|
created_at: now,
|
|
1529
1174
|
started_at: now,
|
|
@@ -1549,8 +1194,14 @@ export class LocalServiceRuntime {
|
|
|
1549
1194
|
}),
|
|
1550
1195
|
events: [],
|
|
1551
1196
|
});
|
|
1552
|
-
this.writeCompileRun(compiledPath, run);
|
|
1553
|
-
|
|
1197
|
+
this.writeCompileRun(prepDataDir, compiledPath, run);
|
|
1198
|
+
this.activeCompileRuns.set(runId, {
|
|
1199
|
+
prepDataDir,
|
|
1200
|
+
compiledPath,
|
|
1201
|
+
preparation: preparationConfig.name,
|
|
1202
|
+
cancelled: false,
|
|
1203
|
+
});
|
|
1204
|
+
await this.recordCompileRunEvent(prepDataDir, compiledPath, runId, {
|
|
1554
1205
|
type: "run.started",
|
|
1555
1206
|
event_id: createRunEventId("event"),
|
|
1556
1207
|
run_id: runId,
|
|
@@ -1561,11 +1212,11 @@ export class LocalServiceRuntime {
|
|
|
1561
1212
|
backend: "native",
|
|
1562
1213
|
});
|
|
1563
1214
|
const sink = {
|
|
1564
|
-
emit: (event) => this.recordCompileRunEvent(compiledPath, runId, event),
|
|
1215
|
+
emit: (event) => this.recordCompileRunEvent(prepDataDir, compiledPath, runId, event),
|
|
1565
1216
|
};
|
|
1566
|
-
void this.runCompileInBackground(request, {
|
|
1217
|
+
void this.runCompileInBackground(prepDataDir, request, {
|
|
1567
1218
|
runId,
|
|
1568
|
-
sourcePath:
|
|
1219
|
+
sourcePath: prepDataDir,
|
|
1569
1220
|
compiledPath,
|
|
1570
1221
|
preparationConfig,
|
|
1571
1222
|
events: sink,
|
|
@@ -1573,56 +1224,198 @@ export class LocalServiceRuntime {
|
|
|
1573
1224
|
const saved = this.readCompileRun(compiledPath, runId) ?? run;
|
|
1574
1225
|
return CompileRunResourceSchema.parse({ run: saved });
|
|
1575
1226
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1227
|
+
/**
|
|
1228
|
+
* Cancel an in-flight compile run. Marks the persisted record as
|
|
1229
|
+
* `cancelled`, emits a `run.failed` event to capture the cancellation in
|
|
1230
|
+
* the run timeline, and clears the active handle so retries may start a
|
|
1231
|
+
* fresh run. If the run already finished, returns
|
|
1232
|
+
* `{ cancelled: false, reason: "already finished" }` and persists nothing.
|
|
1233
|
+
*/
|
|
1234
|
+
cancelCompileRun(runId) {
|
|
1235
|
+
const handle = this.activeCompileRuns.get(runId);
|
|
1236
|
+
if (!handle) {
|
|
1237
|
+
// Either unknown or already terminal. The server route already 404s
|
|
1238
|
+
// unknown ids before calling this, so anything reaching here is a run
|
|
1239
|
+
// we already finalized.
|
|
1240
|
+
return { cancelled: false, reason: "already finished" };
|
|
1241
|
+
}
|
|
1242
|
+
if (handle.cancelled) {
|
|
1243
|
+
return { cancelled: false, reason: "already cancelled" };
|
|
1244
|
+
}
|
|
1245
|
+
const cancelledAt = new Date().toISOString();
|
|
1246
|
+
handle.cancelled = true;
|
|
1247
|
+
handle.cancelledAt = cancelledAt;
|
|
1248
|
+
const current = this.readCompileRun(handle.compiledPath, runId);
|
|
1249
|
+
if (current && current.status !== "succeeded" && current.status !== "failed" && current.status !== "cancelled") {
|
|
1250
|
+
const cancelledRun = {
|
|
1251
|
+
...current,
|
|
1252
|
+
status: "cancelled",
|
|
1253
|
+
finished_at: current.finished_at ?? cancelledAt,
|
|
1254
|
+
events: [
|
|
1255
|
+
...current.events,
|
|
1256
|
+
{
|
|
1257
|
+
type: "run.failed",
|
|
1258
|
+
event_id: createRunEventId("event"),
|
|
1259
|
+
run_id: runId,
|
|
1260
|
+
timestamp: cancelledAt,
|
|
1261
|
+
error: "Compile run cancelled by request.",
|
|
1262
|
+
},
|
|
1263
|
+
],
|
|
1264
|
+
};
|
|
1265
|
+
this.writeCompileRun(handle.prepDataDir, handle.compiledPath, cancelledRun);
|
|
1266
|
+
}
|
|
1267
|
+
return { cancelled: true };
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* Look up the run id previously associated with this idempotency key in
|
|
1271
|
+
* `prepDataDir`. Returns null when the key is unknown or its TTL has
|
|
1272
|
+
* elapsed. The preparation argument is required so that the same key in
|
|
1273
|
+
* two different preparations always returns two different runs.
|
|
1274
|
+
*/
|
|
1275
|
+
findIdempotentCompileRun(prepDataDir, key) {
|
|
1276
|
+
const resolvedRoot = resolve(prepDataDir);
|
|
1277
|
+
const bucket = this.idempotencyKeyCache.get(resolvedRoot);
|
|
1278
|
+
if (!bucket)
|
|
1279
|
+
return null;
|
|
1280
|
+
const entry = bucket.get(key);
|
|
1281
|
+
if (!entry)
|
|
1282
|
+
return null;
|
|
1283
|
+
if (entry.expiresAt <= Date.now()) {
|
|
1284
|
+
// Opportunistic single-key prune. The bulk prune runs on writes
|
|
1285
|
+
// when the cache crosses the size threshold (see
|
|
1286
|
+
// {@link recordIdempotentCompileRun}).
|
|
1287
|
+
bucket.delete(key);
|
|
1288
|
+
if (bucket.size === 0)
|
|
1289
|
+
this.idempotencyKeyCache.delete(resolvedRoot);
|
|
1290
|
+
return null;
|
|
1291
|
+
}
|
|
1292
|
+
return entry.runId;
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Cache the run id created (or returned) for this idempotency key in
|
|
1296
|
+
* `prepDataDir`. Entries expire after `IDEMPOTENCY_TTL_MS`. Pruning
|
|
1297
|
+
* is opportunistic: the previous implementation walked every entry on
|
|
1298
|
+
* every read AND write, which was O(N) per request. Now we only sweep
|
|
1299
|
+
* when the cache grows past {@link IDEMPOTENCY_PRUNE_THRESHOLD}.
|
|
1300
|
+
*/
|
|
1301
|
+
recordIdempotentCompileRun(prepDataDir, key, runId) {
|
|
1302
|
+
const resolvedRoot = resolve(prepDataDir);
|
|
1303
|
+
let bucket = this.idempotencyKeyCache.get(resolvedRoot);
|
|
1304
|
+
if (!bucket) {
|
|
1305
|
+
bucket = new Map();
|
|
1306
|
+
this.idempotencyKeyCache.set(resolvedRoot, bucket);
|
|
1307
|
+
}
|
|
1308
|
+
bucket.set(key, {
|
|
1309
|
+
runId,
|
|
1310
|
+
expiresAt: Date.now() + IDEMPOTENCY_TTL_MS,
|
|
1311
|
+
});
|
|
1312
|
+
if (this.totalIdempotencyEntries() > IDEMPOTENCY_PRUNE_THRESHOLD) {
|
|
1313
|
+
this.pruneIdempotencyKeyCache();
|
|
1314
|
+
}
|
|
1579
1315
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1316
|
+
/** Total cached idempotency entries across all preparations. */
|
|
1317
|
+
totalIdempotencyEntries() {
|
|
1318
|
+
let total = 0;
|
|
1319
|
+
for (const bucket of this.idempotencyKeyCache.values())
|
|
1320
|
+
total += bucket.size;
|
|
1321
|
+
return total;
|
|
1322
|
+
}
|
|
1323
|
+
pruneIdempotencyKeyCache() {
|
|
1324
|
+
const now = Date.now();
|
|
1325
|
+
for (const [prepDataDir, bucket] of this.idempotencyKeyCache) {
|
|
1326
|
+
for (const [key, entry] of bucket) {
|
|
1327
|
+
if (entry.expiresAt <= now)
|
|
1328
|
+
bucket.delete(key);
|
|
1329
|
+
}
|
|
1330
|
+
if (bucket.size === 0)
|
|
1331
|
+
this.idempotencyKeyCache.delete(prepDataDir);
|
|
1332
|
+
}
|
|
1585
1333
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1334
|
+
/**
|
|
1335
|
+
* Test seam: force the cached entry for `key` in `prepDataDir` to be
|
|
1336
|
+
* expired so the next lookup returns null. Returns true when an entry was
|
|
1337
|
+
* found and expired. Tests use this in place of fake timers because the
|
|
1338
|
+
* idempotency TTL is one hour and faking `Date.now()` would destabilize
|
|
1339
|
+
* unrelated runtime state.
|
|
1340
|
+
*/
|
|
1341
|
+
expireIdempotencyKeyForTesting(prepDataDir, key) {
|
|
1342
|
+
const bucket = this.idempotencyKeyCache.get(resolve(prepDataDir));
|
|
1343
|
+
if (!bucket)
|
|
1344
|
+
return false;
|
|
1345
|
+
const entry = bucket.get(key);
|
|
1346
|
+
if (!entry)
|
|
1347
|
+
return false;
|
|
1348
|
+
entry.expiresAt = Date.now() - 1;
|
|
1349
|
+
return true;
|
|
1350
|
+
}
|
|
1351
|
+
listVerifyRuns(prepDataDir) {
|
|
1352
|
+
return newestFirst(listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))
|
|
1353
|
+
.flatMap((preparation) => this.listVerifyRunsForPreparation(prepDataDir, preparation.name)));
|
|
1354
|
+
}
|
|
1355
|
+
listVerifyRunsForPreparation(prepDataDir, preparationName) {
|
|
1356
|
+
return this.verifyRunCache.get(prepDataDir, preparationName, () => {
|
|
1357
|
+
const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparationName);
|
|
1358
|
+
return newestFirst(listJsonFiles(verifyRunsRoot(compiledPath))
|
|
1359
|
+
.map(readVerifyRunAt)
|
|
1360
|
+
.filter((run) => run !== null));
|
|
1361
|
+
}, (run) => run.run_id);
|
|
1362
|
+
}
|
|
1363
|
+
getVerifyRun(prepDataDir, runId) {
|
|
1364
|
+
const known = this.verifyRunCache.preparationFor(prepDataDir, runId);
|
|
1365
|
+
if (known) {
|
|
1366
|
+
const found = this.listVerifyRunsForPreparation(prepDataDir, known).find((run) => run.run_id === runId);
|
|
1367
|
+
if (found)
|
|
1368
|
+
return found;
|
|
1369
|
+
}
|
|
1370
|
+
return this.listVerifyRuns(prepDataDir).find((run) => run.run_id === runId) ?? null;
|
|
1588
1371
|
}
|
|
1589
|
-
async
|
|
1590
|
-
const request =
|
|
1591
|
-
const preparationConfig = this.resolvePreparationConfig(request.preparation);
|
|
1592
|
-
const compiledPath =
|
|
1372
|
+
async createVerifyRun(prepDataDir, requestValue) {
|
|
1373
|
+
const request = VerifyRunCreateRequestSchema.parse(requestValue);
|
|
1374
|
+
const preparationConfig = this.resolvePreparationConfig(prepDataDir, request.preparation);
|
|
1375
|
+
const compiledPath = preparationPortableContextPath(asPreparationDataDir(prepDataDir), preparationConfig.name);
|
|
1593
1376
|
const compiledTarget = createCompiledTestTarget(compiledPath, preparationConfig.name, methodIdForSourcePreparationConfig(preparationConfig) ?? DEFAULT_METHOD_ID);
|
|
1594
|
-
const runId = createRunId("
|
|
1377
|
+
const runId = createRunId("verify");
|
|
1595
1378
|
const now = new Date().toISOString();
|
|
1596
|
-
const initial =
|
|
1379
|
+
const initial = VerifyRunResourceSchema.parse({
|
|
1597
1380
|
run_id: runId,
|
|
1598
1381
|
status: "running",
|
|
1599
1382
|
preparation: preparationConfig.name,
|
|
1600
|
-
|
|
1601
|
-
source_path: this.rootPath,
|
|
1383
|
+
source_path: prepDataDir,
|
|
1602
1384
|
portable_context_path: compiledTarget.eligible ? compiledPath : null,
|
|
1603
1385
|
started_at: now,
|
|
1604
1386
|
readiness_run: null,
|
|
1605
1387
|
events: [],
|
|
1606
1388
|
});
|
|
1607
|
-
this.
|
|
1608
|
-
void this.runTestInBackground(request, {
|
|
1389
|
+
this.writeVerifyRun(prepDataDir, compiledPath, initial);
|
|
1390
|
+
void this.runTestInBackground(prepDataDir, request, {
|
|
1609
1391
|
runId,
|
|
1610
|
-
sourcePath:
|
|
1392
|
+
sourcePath: prepDataDir,
|
|
1611
1393
|
compiledPath,
|
|
1612
1394
|
preparationConfig,
|
|
1613
1395
|
}, initial);
|
|
1614
1396
|
return initial;
|
|
1615
1397
|
}
|
|
1616
|
-
async runCompileInBackground(request, context) {
|
|
1398
|
+
async runCompileInBackground(prepDataDir, request, context) {
|
|
1399
|
+
this.beginActiveRun();
|
|
1617
1400
|
try {
|
|
1618
1401
|
if (!this.handlers.createCompileRun) {
|
|
1619
1402
|
throw new Error("No compile-run handler is configured for this local service.");
|
|
1620
1403
|
}
|
|
1621
1404
|
const result = LocalRunHandlerResultSchema.parse(await this.handlers.createCompileRun(request, context));
|
|
1622
|
-
this.
|
|
1623
|
-
|
|
1405
|
+
const wasCancelled = this.activeCompileRuns.get(context.runId)?.cancelled === true;
|
|
1406
|
+
if (wasCancelled) {
|
|
1407
|
+
// The run was cancelled while the handler was still running. The
|
|
1408
|
+
// cancellation path already wrote a `cancelled` record; just refresh
|
|
1409
|
+
// observability and skip emitting a second terminal event.
|
|
1410
|
+
this.refreshCompileRunFromRuntime(prepDataDir, context.compiledPath, context.runId);
|
|
1411
|
+
await this.emitRuntimeDerivedEvents(prepDataDir, context.compiledPath, context.runId);
|
|
1412
|
+
await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(prepDataDir, context.preparationConfig)));
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
this.refreshCompileRunFromRuntime(prepDataDir, context.compiledPath, context.runId);
|
|
1416
|
+
await this.emitRuntimeDerivedEvents(prepDataDir, context.compiledPath, context.runId);
|
|
1624
1417
|
if (!result.ok) {
|
|
1625
|
-
await this.recordCompileRunEvent(context.compiledPath, context.runId, {
|
|
1418
|
+
await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, {
|
|
1626
1419
|
type: "run.failed",
|
|
1627
1420
|
event_id: createRunEventId("event"),
|
|
1628
1421
|
run_id: context.runId,
|
|
@@ -1631,7 +1424,7 @@ export class LocalServiceRuntime {
|
|
|
1631
1424
|
});
|
|
1632
1425
|
}
|
|
1633
1426
|
else {
|
|
1634
|
-
await this.recordCompileRunEvent(context.compiledPath, context.runId, {
|
|
1427
|
+
await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, {
|
|
1635
1428
|
type: "run.completed",
|
|
1636
1429
|
event_id: createRunEventId("event"),
|
|
1637
1430
|
run_id: context.runId,
|
|
@@ -1639,30 +1432,35 @@ export class LocalServiceRuntime {
|
|
|
1639
1432
|
summary: "Portable context ready.",
|
|
1640
1433
|
});
|
|
1641
1434
|
}
|
|
1642
|
-
await this.recordCompileRunEvent(context.compiledPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(context.preparationConfig)));
|
|
1435
|
+
await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(prepDataDir, context.preparationConfig)));
|
|
1643
1436
|
}
|
|
1644
1437
|
catch (error) {
|
|
1645
|
-
await this.recordCompileRunEvent(context.compiledPath, context.runId, {
|
|
1438
|
+
await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, {
|
|
1646
1439
|
type: "run.failed",
|
|
1647
1440
|
event_id: createRunEventId("event"),
|
|
1648
1441
|
run_id: context.runId,
|
|
1649
1442
|
timestamp: createRunEventTimestamp(),
|
|
1650
1443
|
error: error instanceof Error ? error.message : String(error),
|
|
1651
1444
|
});
|
|
1652
|
-
await this.recordCompileRunEvent(context.compiledPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(context.preparationConfig)));
|
|
1445
|
+
await this.recordCompileRunEvent(prepDataDir, context.compiledPath, context.runId, this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, this.computePreparationReadiness(prepDataDir, context.preparationConfig)));
|
|
1446
|
+
}
|
|
1447
|
+
finally {
|
|
1448
|
+
this.activeCompileRuns.delete(context.runId);
|
|
1449
|
+
this.endActiveRun();
|
|
1653
1450
|
}
|
|
1654
1451
|
}
|
|
1655
|
-
async runTestInBackground(request, context, initial) {
|
|
1452
|
+
async runTestInBackground(prepDataDir, request, context, initial) {
|
|
1453
|
+
this.beginActiveRun();
|
|
1656
1454
|
try {
|
|
1657
|
-
if (!this.handlers.
|
|
1658
|
-
throw new Error("No
|
|
1455
|
+
if (!this.handlers.createVerifyRun) {
|
|
1456
|
+
throw new Error("No verify-run handler is configured for this local service.");
|
|
1659
1457
|
}
|
|
1660
|
-
const result = LocalRunHandlerResultSchema.parse(await this.handlers.
|
|
1661
|
-
const readinessRun = result.readiness_run ?? this.readLatestReadinessRun(context.preparationConfig.name);
|
|
1458
|
+
const result = LocalRunHandlerResultSchema.parse(await this.handlers.createVerifyRun(request, context));
|
|
1459
|
+
const readinessRun = result.readiness_run ?? this.readLatestReadinessRun(prepDataDir, context.preparationConfig.name);
|
|
1662
1460
|
const resultEvent = readinessRun
|
|
1663
1461
|
? this.checksEvaluatedEvent(context.runId, readinessRun)
|
|
1664
1462
|
: null;
|
|
1665
|
-
const nextWithoutReadiness =
|
|
1463
|
+
const nextWithoutReadiness = VerifyRunResourceSchema.parse({
|
|
1666
1464
|
...initial,
|
|
1667
1465
|
status: result.ok ? "succeeded" : "failed",
|
|
1668
1466
|
finished_at: new Date().toISOString(),
|
|
@@ -1670,9 +1468,9 @@ export class LocalServiceRuntime {
|
|
|
1670
1468
|
events: resultEvent ? [resultEvent] : [],
|
|
1671
1469
|
...(!result.ok ? { error: result.error ?? "Readiness check failed." } : {}),
|
|
1672
1470
|
});
|
|
1673
|
-
this.
|
|
1674
|
-
const readiness = this.computePreparationReadiness(context.preparationConfig);
|
|
1675
|
-
const next =
|
|
1471
|
+
this.writeVerifyRun(prepDataDir, context.compiledPath, nextWithoutReadiness);
|
|
1472
|
+
const readiness = this.computePreparationReadiness(prepDataDir, context.preparationConfig);
|
|
1473
|
+
const next = VerifyRunResourceSchema.parse({
|
|
1676
1474
|
...nextWithoutReadiness,
|
|
1677
1475
|
readiness,
|
|
1678
1476
|
events: [
|
|
@@ -1680,32 +1478,41 @@ export class LocalServiceRuntime {
|
|
|
1680
1478
|
this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, readiness),
|
|
1681
1479
|
],
|
|
1682
1480
|
});
|
|
1683
|
-
this.
|
|
1481
|
+
this.writeVerifyRun(prepDataDir, context.compiledPath, next);
|
|
1684
1482
|
}
|
|
1685
1483
|
catch (error) {
|
|
1686
|
-
const failedWithoutReadiness =
|
|
1484
|
+
const failedWithoutReadiness = VerifyRunResourceSchema.parse({
|
|
1687
1485
|
...initial,
|
|
1688
1486
|
status: "failed",
|
|
1689
1487
|
finished_at: new Date().toISOString(),
|
|
1690
1488
|
error: error instanceof Error ? error.message : String(error),
|
|
1691
1489
|
});
|
|
1692
|
-
this.
|
|
1693
|
-
const readiness = this.computePreparationReadiness(context.preparationConfig);
|
|
1694
|
-
const next =
|
|
1490
|
+
this.writeVerifyRun(prepDataDir, context.compiledPath, failedWithoutReadiness);
|
|
1491
|
+
const readiness = this.computePreparationReadiness(prepDataDir, context.preparationConfig);
|
|
1492
|
+
const next = VerifyRunResourceSchema.parse({
|
|
1695
1493
|
...failedWithoutReadiness,
|
|
1696
1494
|
readiness,
|
|
1697
1495
|
events: [this.readinessUpdatedEvent(context.runId, context.preparationConfig.name, readiness)],
|
|
1698
1496
|
});
|
|
1699
|
-
this.
|
|
1497
|
+
this.writeVerifyRun(prepDataDir, context.compiledPath, next);
|
|
1498
|
+
}
|
|
1499
|
+
finally {
|
|
1500
|
+
this.endActiveRun();
|
|
1700
1501
|
}
|
|
1701
1502
|
}
|
|
1702
|
-
async runReadinessCheckDraftInBackground(request, runId) {
|
|
1503
|
+
async runReadinessCheckDraftInBackground(prepDataDir, request, runId) {
|
|
1504
|
+
this.beginActiveRun();
|
|
1505
|
+
return this.runReadinessCheckDraftInBackgroundInner(prepDataDir, request, runId).finally(() => {
|
|
1506
|
+
this.endActiveRun();
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
async runReadinessCheckDraftInBackgroundInner(prepDataDir, request, runId) {
|
|
1703
1510
|
try {
|
|
1704
1511
|
if (!this.handlers.createReadinessCheckDraft) {
|
|
1705
1512
|
throw new Error("No readiness-check-draft handler is configured for this local service.");
|
|
1706
1513
|
}
|
|
1707
|
-
const result = ReadinessCheckDraftResultSchema.parse(await this.handlers.createReadinessCheckDraft(request, this.jobRunContext(runId)));
|
|
1708
|
-
this.appendJobRunEvent(runId, {
|
|
1514
|
+
const result = ReadinessCheckDraftResultSchema.parse(await this.handlers.createReadinessCheckDraft(request, this.jobRunContext(prepDataDir, runId)));
|
|
1515
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1709
1516
|
type: "step.completed",
|
|
1710
1517
|
step_id: "agent-draft",
|
|
1711
1518
|
message: `Drafted ${result.checks.length} readiness checks.`,
|
|
@@ -1713,7 +1520,7 @@ export class LocalServiceRuntime {
|
|
|
1713
1520
|
checks: result.checks,
|
|
1714
1521
|
},
|
|
1715
1522
|
});
|
|
1716
|
-
this.appendJobRunEvent(runId, {
|
|
1523
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1717
1524
|
type: "step.started",
|
|
1718
1525
|
step_id: "normalize-checks",
|
|
1719
1526
|
message: "Normalizing drafted readiness checks into saved check records.",
|
|
@@ -1721,7 +1528,7 @@ export class LocalServiceRuntime {
|
|
|
1721
1528
|
checks: result.checks.length,
|
|
1722
1529
|
},
|
|
1723
1530
|
});
|
|
1724
|
-
this.appendJobRunEvent(runId, {
|
|
1531
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1725
1532
|
type: "step.completed",
|
|
1726
1533
|
step_id: "normalize-checks",
|
|
1727
1534
|
message: `${result.checks.length} readiness checks ready for review.`,
|
|
@@ -1729,15 +1536,15 @@ export class LocalServiceRuntime {
|
|
|
1729
1536
|
checks: result.checks.length,
|
|
1730
1537
|
},
|
|
1731
1538
|
});
|
|
1732
|
-
this.setJobRunResult(runId, result);
|
|
1733
|
-
this.appendJobRunEvent(runId, {
|
|
1539
|
+
this.setJobRunResult(prepDataDir, runId, result);
|
|
1540
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1734
1541
|
type: "job.completed",
|
|
1735
1542
|
message: `Drafted ${result.checks.length} readiness checks.`,
|
|
1736
1543
|
});
|
|
1737
1544
|
}
|
|
1738
1545
|
catch (error) {
|
|
1739
1546
|
const message = error instanceof Error ? error.message : String(error);
|
|
1740
|
-
this.appendJobRunEvent(runId, {
|
|
1547
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1741
1548
|
type: "step.failed",
|
|
1742
1549
|
step_id: "agent-draft",
|
|
1743
1550
|
message,
|
|
@@ -1745,20 +1552,26 @@ export class LocalServiceRuntime {
|
|
|
1745
1552
|
error: message,
|
|
1746
1553
|
},
|
|
1747
1554
|
});
|
|
1748
|
-
this.appendJobRunEvent(runId, {
|
|
1555
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1749
1556
|
type: "job.failed",
|
|
1750
1557
|
message,
|
|
1751
1558
|
});
|
|
1752
1559
|
}
|
|
1753
1560
|
}
|
|
1754
|
-
async runMethodAuthoringInBackground(request, runId) {
|
|
1561
|
+
async runMethodAuthoringInBackground(prepDataDir, request, runId) {
|
|
1562
|
+
this.beginActiveRun();
|
|
1563
|
+
return this.runMethodAuthoringInBackgroundInner(prepDataDir, request, runId).finally(() => {
|
|
1564
|
+
this.endActiveRun();
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
async runMethodAuthoringInBackgroundInner(prepDataDir, request, runId) {
|
|
1755
1568
|
try {
|
|
1756
1569
|
if (!this.handlers.createMethodAuthoringRun) {
|
|
1757
1570
|
throw new Error("No Method-authoring handler is configured for this local service.");
|
|
1758
1571
|
}
|
|
1759
|
-
const result = MethodAuthoringResultSchema.parse(await this.handlers.createMethodAuthoringRun(request, this.jobRunContext(runId)));
|
|
1760
|
-
this.setJobRunResult(runId, result);
|
|
1761
|
-
this.appendJobRunEvent(runId, {
|
|
1572
|
+
const result = MethodAuthoringResultSchema.parse(await this.handlers.createMethodAuthoringRun(request, this.jobRunContext(prepDataDir, runId)));
|
|
1573
|
+
this.setJobRunResult(prepDataDir, runId, result);
|
|
1574
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1762
1575
|
type: result.status === "executor-failed" ? "step.failed" : "step.completed",
|
|
1763
1576
|
step_id: "draft-package",
|
|
1764
1577
|
message: result.summary,
|
|
@@ -1769,7 +1582,7 @@ export class LocalServiceRuntime {
|
|
|
1769
1582
|
shell_path: result.shell_path,
|
|
1770
1583
|
},
|
|
1771
1584
|
});
|
|
1772
|
-
this.appendJobRunEvent(runId, {
|
|
1585
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1773
1586
|
type: "step.started",
|
|
1774
1587
|
step_id: "validate-package",
|
|
1775
1588
|
message: "Validating Method package structure and stage contract.",
|
|
@@ -1778,7 +1591,7 @@ export class LocalServiceRuntime {
|
|
|
1778
1591
|
},
|
|
1779
1592
|
});
|
|
1780
1593
|
if (result.status === "updated" || result.status === "no-change") {
|
|
1781
|
-
this.appendJobRunEvent(runId, {
|
|
1594
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1782
1595
|
type: "step.completed",
|
|
1783
1596
|
step_id: "validate-package",
|
|
1784
1597
|
message: result.summary,
|
|
@@ -1787,13 +1600,13 @@ export class LocalServiceRuntime {
|
|
|
1787
1600
|
validation: result.validation ?? null,
|
|
1788
1601
|
},
|
|
1789
1602
|
});
|
|
1790
|
-
this.appendJobRunEvent(runId, {
|
|
1603
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1791
1604
|
type: "job.completed",
|
|
1792
1605
|
message: result.summary,
|
|
1793
1606
|
});
|
|
1794
1607
|
}
|
|
1795
1608
|
else {
|
|
1796
|
-
this.appendJobRunEvent(runId, {
|
|
1609
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1797
1610
|
type: "step.failed",
|
|
1798
1611
|
step_id: "validate-package",
|
|
1799
1612
|
message: result.summary,
|
|
@@ -1802,7 +1615,7 @@ export class LocalServiceRuntime {
|
|
|
1802
1615
|
validation: result.validation ?? null,
|
|
1803
1616
|
},
|
|
1804
1617
|
});
|
|
1805
|
-
this.appendJobRunEvent(runId, {
|
|
1618
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1806
1619
|
type: "job.failed",
|
|
1807
1620
|
message: result.summary,
|
|
1808
1621
|
});
|
|
@@ -1810,7 +1623,7 @@ export class LocalServiceRuntime {
|
|
|
1810
1623
|
}
|
|
1811
1624
|
catch (error) {
|
|
1812
1625
|
const message = error instanceof Error ? error.message : String(error);
|
|
1813
|
-
this.appendJobRunEvent(runId, {
|
|
1626
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1814
1627
|
type: "step.failed",
|
|
1815
1628
|
step_id: "draft-package",
|
|
1816
1629
|
message,
|
|
@@ -1818,29 +1631,29 @@ export class LocalServiceRuntime {
|
|
|
1818
1631
|
error: message,
|
|
1819
1632
|
},
|
|
1820
1633
|
});
|
|
1821
|
-
this.appendJobRunEvent(runId, {
|
|
1634
|
+
this.appendJobRunEvent(prepDataDir, runId, {
|
|
1822
1635
|
type: "job.failed",
|
|
1823
1636
|
message,
|
|
1824
1637
|
});
|
|
1825
1638
|
}
|
|
1826
1639
|
}
|
|
1827
|
-
jobRunContext(runId) {
|
|
1640
|
+
jobRunContext(prepDataDir, runId) {
|
|
1828
1641
|
return {
|
|
1829
1642
|
runId,
|
|
1830
|
-
sourcePath:
|
|
1643
|
+
sourcePath: prepDataDir,
|
|
1831
1644
|
emit: (event) => {
|
|
1832
|
-
this.appendJobRunEvent(runId, event);
|
|
1645
|
+
this.appendJobRunEvent(prepDataDir, runId, event);
|
|
1833
1646
|
},
|
|
1834
1647
|
};
|
|
1835
1648
|
}
|
|
1836
|
-
defaultPreparationName() {
|
|
1837
|
-
const preparation = listSourcePreparationConfigs(loadSourceFolderConfig(
|
|
1649
|
+
defaultPreparationName(prepDataDir) {
|
|
1650
|
+
const preparation = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))[0];
|
|
1838
1651
|
if (!preparation) {
|
|
1839
1652
|
throw new Error("No Preparation is saved in this control plane folder.");
|
|
1840
1653
|
}
|
|
1841
1654
|
return preparation.name;
|
|
1842
1655
|
}
|
|
1843
|
-
async planActionProposal(request) {
|
|
1656
|
+
async planActionProposal(prepDataDir, request) {
|
|
1844
1657
|
if (!this.handlers.planActionProposal) {
|
|
1845
1658
|
return ActionProposalPlanSchema.parse({
|
|
1846
1659
|
action_type: "clarification",
|
|
@@ -1848,15 +1661,15 @@ export class LocalServiceRuntime {
|
|
|
1848
1661
|
assistant_message: "No local action planner is configured for this Interf Workspace.",
|
|
1849
1662
|
});
|
|
1850
1663
|
}
|
|
1851
|
-
const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(
|
|
1664
|
+
const preparations = listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir));
|
|
1852
1665
|
let rawPlan;
|
|
1853
1666
|
try {
|
|
1854
1667
|
rawPlan = await this.handlers.planActionProposal(request, {
|
|
1855
|
-
sourcePath:
|
|
1668
|
+
sourcePath: prepDataDir,
|
|
1856
1669
|
preparations,
|
|
1857
1670
|
preparationHealth: preparations.map((preparation) => {
|
|
1858
1671
|
const readinessChecks = preparation.checks?.length ?? 0;
|
|
1859
|
-
const portableContextReady = hasCompiledTestTarget(
|
|
1672
|
+
const portableContextReady = hasCompiledTestTarget(prepDataDir, preparation);
|
|
1860
1673
|
return {
|
|
1861
1674
|
name: preparation.name,
|
|
1862
1675
|
readiness_checks: readinessChecks,
|
|
@@ -1869,8 +1682,8 @@ export class LocalServiceRuntime {
|
|
|
1869
1682
|
: ["prepare"],
|
|
1870
1683
|
};
|
|
1871
1684
|
}),
|
|
1872
|
-
sourceFolders: listSourceFolderChoices(
|
|
1873
|
-
recentProposals: this.listActionProposals().slice(0, 5),
|
|
1685
|
+
sourceFolders: listSourceFolderChoices(prepDataDir),
|
|
1686
|
+
recentProposals: this.listActionProposals(prepDataDir).slice(0, 5),
|
|
1874
1687
|
});
|
|
1875
1688
|
}
|
|
1876
1689
|
catch {
|
|
@@ -1927,7 +1740,7 @@ export class LocalServiceRuntime {
|
|
|
1927
1740
|
error: null,
|
|
1928
1741
|
});
|
|
1929
1742
|
}
|
|
1930
|
-
async buildActionProposal(request) {
|
|
1743
|
+
async buildActionProposal(prepDataDir, request) {
|
|
1931
1744
|
const structuredPreparationSetup = PreparationSetupActionValuesSchema.safeParse(request.values);
|
|
1932
1745
|
const structuredMethodAuthoring = MethodAuthoringActionValuesSchema.safeParse(request.values);
|
|
1933
1746
|
const structuredActionType = actionTypeFromValues(request.values);
|
|
@@ -1952,7 +1765,7 @@ export class LocalServiceRuntime {
|
|
|
1952
1765
|
action_type: structuredPlanActionType,
|
|
1953
1766
|
...(request.preparation ? { preparation: request.preparation } : {}),
|
|
1954
1767
|
})
|
|
1955
|
-
: await this.planActionProposal(request);
|
|
1768
|
+
: await this.planActionProposal(prepDataDir, request);
|
|
1956
1769
|
const actionType = structuredPlanActionType ?? plan.action_type;
|
|
1957
1770
|
if (directServiceEndpointForAction(actionType)) {
|
|
1958
1771
|
return this.directServiceActionClarification({
|
|
@@ -2003,13 +1816,13 @@ export class LocalServiceRuntime {
|
|
|
2003
1816
|
const requestedPreparationName = plan.preparation ?? request.preparation ?? null;
|
|
2004
1817
|
const fallbackPreparation = requestedPreparationName
|
|
2005
1818
|
? null
|
|
2006
|
-
: listSourcePreparationConfigs(loadSourceFolderConfig(
|
|
1819
|
+
: listSourcePreparationConfigs(loadSourceFolderConfig(prepDataDir))[0] ?? null;
|
|
2007
1820
|
const preparationConfig = requestedPreparationName
|
|
2008
|
-
? this.resolvePreparationConfig(requestedPreparationName)
|
|
1821
|
+
? this.resolvePreparationConfig(prepDataDir, requestedPreparationName)
|
|
2009
1822
|
: fallbackPreparation;
|
|
2010
1823
|
const preparationPath = preparationConfig
|
|
2011
|
-
? resolveSourcePreparationPath(
|
|
2012
|
-
: resolveConfiguredSourceFolderPath(
|
|
1824
|
+
? resolveSourcePreparationPath(prepDataDir, preparationConfig)
|
|
1825
|
+
: resolveConfiguredSourceFolderPath(prepDataDir) ?? prepDataDir;
|
|
2013
1826
|
const requestedMethodId = stringValue(request.values, "method_id") ??
|
|
2014
1827
|
stringValue(request.values, "method");
|
|
2015
1828
|
const plannedMethodId = plan.method ??
|
|
@@ -2057,9 +1870,9 @@ export class LocalServiceRuntime {
|
|
|
2057
1870
|
error: null,
|
|
2058
1871
|
});
|
|
2059
1872
|
}
|
|
2060
|
-
const preparationConfig = this.resolvePreparationConfig(plan.preparation ?? request.preparation ?? this.defaultPreparationName());
|
|
1873
|
+
const preparationConfig = this.resolvePreparationConfig(prepDataDir, plan.preparation ?? request.preparation ?? this.defaultPreparationName(prepDataDir));
|
|
2061
1874
|
const proposalActionType = ActionProposalTypeSchema.parse(actionType);
|
|
2062
|
-
const preparationPath = resolveSourcePreparationPath(
|
|
1875
|
+
const preparationPath = resolveSourcePreparationPath(prepDataDir, preparationConfig);
|
|
2063
1876
|
const requestedMethodId = stringValue(request.values, "method_id") ??
|
|
2064
1877
|
stringValue(request.values, "method");
|
|
2065
1878
|
const plannedMethodId = plan.method ??
|
|
@@ -2093,7 +1906,7 @@ export class LocalServiceRuntime {
|
|
|
2093
1906
|
if (actionType === "test") {
|
|
2094
1907
|
const requestedMode = testModeFromValues(proposalValues);
|
|
2095
1908
|
const hasReadinessChecks = (preparationConfig.checks ?? []).length > 0;
|
|
2096
|
-
const portableContextReady = hasCompiledTestTarget(
|
|
1909
|
+
const portableContextReady = hasCompiledTestTarget(prepDataDir, preparationConfig);
|
|
2097
1910
|
if (!hasReadinessChecks) {
|
|
2098
1911
|
return clarifyResolvedAction({
|
|
2099
1912
|
title: `Add readiness checks for ${preparationConfig.name}`,
|
|
@@ -2101,7 +1914,7 @@ export class LocalServiceRuntime {
|
|
|
2101
1914
|
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.`,
|
|
2102
1915
|
});
|
|
2103
1916
|
}
|
|
2104
|
-
if (!portableContextReady && requestedMode !== "
|
|
1917
|
+
if (!portableContextReady && requestedMode !== "source-files") {
|
|
2105
1918
|
return clarifyResolvedAction({
|
|
2106
1919
|
title: `Prepare ${preparationConfig.name} first`,
|
|
2107
1920
|
summary: "Readiness checks need portable context unless you explicitly ask for a source-files-only baseline.",
|
|
@@ -2117,10 +1930,11 @@ export class LocalServiceRuntime {
|
|
|
2117
1930
|
};
|
|
2118
1931
|
}
|
|
2119
1932
|
if (actionType === "test") {
|
|
2120
|
-
|
|
1933
|
+
// 0.15 — verify runs always judge against the compiled
|
|
1934
|
+
// portable context. The legacy `mode` field is gone from the
|
|
1935
|
+
// wire request shape.
|
|
2121
1936
|
return {
|
|
2122
1937
|
preparation: preparationConfig.name,
|
|
2123
|
-
mode: testModeValue(proposalValues, defaultMode),
|
|
2124
1938
|
};
|
|
2125
1939
|
}
|
|
2126
1940
|
if (actionType === "readiness-check-draft") {
|
|
@@ -2170,9 +1984,7 @@ export class LocalServiceRuntime {
|
|
|
2170
1984
|
return "Ask the configured local executor to draft saved readiness checks.";
|
|
2171
1985
|
return "Ask the configured local executor to create a reusable local Method.";
|
|
2172
1986
|
})();
|
|
2173
|
-
const previewValues =
|
|
2174
|
-
? { mode: actionRequest.mode }
|
|
2175
|
-
: proposalValues;
|
|
1987
|
+
const previewValues = proposalValues;
|
|
2176
1988
|
const commandPreview = plan.command_preview ?? actionCommandPreview(proposalActionType, preparationConfig.name, methodId, previewValues);
|
|
2177
1989
|
return ActionProposalResourceSchema.parse({
|
|
2178
1990
|
kind: "interf-action-proposal",
|
|
@@ -2197,26 +2009,26 @@ export class LocalServiceRuntime {
|
|
|
2197
2009
|
error: null,
|
|
2198
2010
|
});
|
|
2199
2011
|
}
|
|
2200
|
-
async submitActionProposal(proposal) {
|
|
2012
|
+
async submitActionProposal(prepDataDir, proposal) {
|
|
2201
2013
|
if (proposal.action_type === "clarification") {
|
|
2202
2014
|
throw new Error("Clarification proposals cannot be submitted.");
|
|
2203
2015
|
}
|
|
2204
2016
|
if (proposal.action_type === "compile") {
|
|
2205
|
-
const resource = await this.createCompileRun(proposal.request);
|
|
2017
|
+
const resource = await this.createCompileRun(prepDataDir, proposal.request);
|
|
2206
2018
|
return {
|
|
2207
2019
|
runId: resource.run.run_id,
|
|
2208
2020
|
runType: "compile-run",
|
|
2209
2021
|
};
|
|
2210
2022
|
}
|
|
2211
2023
|
if (proposal.action_type === "test") {
|
|
2212
|
-
const resource = await this.
|
|
2024
|
+
const resource = await this.createVerifyRun(prepDataDir, proposal.request);
|
|
2213
2025
|
return {
|
|
2214
2026
|
runId: resource.run_id,
|
|
2215
|
-
runType: "
|
|
2027
|
+
runType: "verify-run",
|
|
2216
2028
|
};
|
|
2217
2029
|
}
|
|
2218
2030
|
if (proposal.action_type === "readiness-check-draft") {
|
|
2219
|
-
const job = await this.createReadinessCheckDraftRun(proposal.request);
|
|
2031
|
+
const job = await this.createReadinessCheckDraftRun(prepDataDir, proposal.request);
|
|
2220
2032
|
return {
|
|
2221
2033
|
runId: job.run_id,
|
|
2222
2034
|
runType: "job-run",
|
|
@@ -2226,14 +2038,14 @@ export class LocalServiceRuntime {
|
|
|
2226
2038
|
if (directEndpoint) {
|
|
2227
2039
|
throw new Error(`Action "${proposal.action_type}" must be submitted directly to ${directEndpoint}.`);
|
|
2228
2040
|
}
|
|
2229
|
-
const job = await this.createMethodAuthoringRun(proposal.request, proposal.action_type === "method-improvement" ? "method-improvement" : "method-authoring");
|
|
2041
|
+
const job = await this.createMethodAuthoringRun(prepDataDir, proposal.request, proposal.action_type === "method-improvement" ? "method-improvement" : "method-authoring");
|
|
2230
2042
|
return {
|
|
2231
2043
|
runId: job.run_id,
|
|
2232
2044
|
runType: "job-run",
|
|
2233
2045
|
};
|
|
2234
2046
|
}
|
|
2235
|
-
resolvePreparationConfig(preparationName, overrides = {}) {
|
|
2236
|
-
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(
|
|
2047
|
+
resolvePreparationConfig(prepDataDir, preparationName, overrides = {}) {
|
|
2048
|
+
const preparation = findSourcePreparationConfig(loadSourceFolderConfig(prepDataDir), preparationName);
|
|
2237
2049
|
if (!preparation) {
|
|
2238
2050
|
throw new Error(`Preparation "${preparationName}" is not saved in this control plane folder.`);
|
|
2239
2051
|
}
|
|
@@ -2245,49 +2057,59 @@ export class LocalServiceRuntime {
|
|
|
2245
2057
|
...(typeof overrides.max_loops === "number" ? { max_loops: overrides.max_loops } : {}),
|
|
2246
2058
|
};
|
|
2247
2059
|
}
|
|
2248
|
-
ensureCompiledForRun(preparationConfig) {
|
|
2060
|
+
ensureCompiledForRun(prepDataDir, preparationConfig) {
|
|
2249
2061
|
const methodId = requireSelectedMethod(preparationConfig);
|
|
2250
|
-
const compiledPath = ensurePortableContextScaffold(
|
|
2062
|
+
const compiledPath = ensurePortableContextScaffold(prepDataDir, preparationConfig.name, methodId);
|
|
2251
2063
|
syncCompiledInterfConfigFromSourcePreparationConfig(compiledPath, preparationConfig);
|
|
2252
2064
|
return compiledPath;
|
|
2253
2065
|
}
|
|
2254
2066
|
readCompileRun(compiledPath, runId) {
|
|
2255
2067
|
return readCompileRunAt(compileRunPath(compiledPath, runId));
|
|
2256
2068
|
}
|
|
2257
|
-
writeCompileRun(compiledPath, run) {
|
|
2069
|
+
writeCompileRun(prepDataDir, compiledPath, run) {
|
|
2258
2070
|
mkdirSync(compileRunsRoot(compiledPath), { recursive: true });
|
|
2259
2071
|
writeJsonFile(compileRunPath(compiledPath, run.run_id), CompileRunSchema.parse(run));
|
|
2072
|
+
// Bust per-preparation list + readiness caches so the next read
|
|
2073
|
+
// reflects the write. We invalidate broadly (per-preparation, not
|
|
2074
|
+
// per-record) because list recompute cost is bounded by run count
|
|
2075
|
+
// and the simpler model avoids fan-out bugs.
|
|
2076
|
+
this.compileRunCache.invalidatePreparation(prepDataDir, run.preparation);
|
|
2077
|
+
this.readinessCache.invalidatePreparation(prepDataDir, run.preparation);
|
|
2078
|
+
}
|
|
2079
|
+
writeJobRun(prepDataDir, run) {
|
|
2080
|
+
mkdirSync(localJobsRoot(prepDataDir), { recursive: true });
|
|
2081
|
+
writeJsonFile(localJobPath(prepDataDir, run.run_id), LocalJobRunResourceSchema.parse(run));
|
|
2082
|
+
if (run.preparation) {
|
|
2083
|
+
// Some job runs (readiness-check drafts) flip readiness state.
|
|
2084
|
+
this.readinessCache.invalidatePreparation(prepDataDir, run.preparation);
|
|
2085
|
+
}
|
|
2260
2086
|
}
|
|
2261
|
-
|
|
2262
|
-
mkdirSync(
|
|
2263
|
-
writeJsonFile(
|
|
2264
|
-
}
|
|
2265
|
-
writeActionProposal(proposal) {
|
|
2266
|
-
mkdirSync(actionProposalsRoot(this.rootPath), { recursive: true });
|
|
2267
|
-
writeJsonFile(actionProposalPath(this.rootPath, proposal.proposal_id), ActionProposalResourceSchema.parse(proposal));
|
|
2087
|
+
writeActionProposal(prepDataDir, proposal) {
|
|
2088
|
+
mkdirSync(actionProposalsRoot(prepDataDir), { recursive: true });
|
|
2089
|
+
writeJsonFile(actionProposalPath(prepDataDir, proposal.proposal_id), ActionProposalResourceSchema.parse(proposal));
|
|
2268
2090
|
}
|
|
2269
|
-
setJobRunResult(runId, result) {
|
|
2270
|
-
const current = this.getJob(runId);
|
|
2091
|
+
setJobRunResult(prepDataDir, runId, result) {
|
|
2092
|
+
const current = this.getJob(prepDataDir, runId);
|
|
2271
2093
|
if (!current)
|
|
2272
2094
|
return;
|
|
2273
2095
|
const normalizedResult = result && typeof result === "object" && !Array.isArray(result)
|
|
2274
2096
|
? result
|
|
2275
2097
|
: { value: result };
|
|
2276
|
-
this.writeJobRun(LocalJobRunResourceSchema.parse({
|
|
2098
|
+
this.writeJobRun(prepDataDir, LocalJobRunResourceSchema.parse({
|
|
2277
2099
|
...current,
|
|
2278
2100
|
result: normalizedResult,
|
|
2279
2101
|
}));
|
|
2280
2102
|
}
|
|
2281
|
-
async recordCompileRunEvent(compiledPath, runId, event) {
|
|
2103
|
+
async recordCompileRunEvent(prepDataDir, compiledPath, runId, event) {
|
|
2282
2104
|
const current = this.readCompileRun(compiledPath, runId);
|
|
2283
2105
|
if (!current)
|
|
2284
2106
|
return;
|
|
2285
|
-
this.writeCompileRun(compiledPath, applyEventToCompileRun(current, event));
|
|
2107
|
+
this.writeCompileRun(prepDataDir, compiledPath, applyEventToCompileRun(current, event));
|
|
2286
2108
|
if (event.type === "stage.passed" || event.type === "stage.failed") {
|
|
2287
|
-
this.refreshCompileRunFromRuntime(compiledPath, runId);
|
|
2109
|
+
this.refreshCompileRunFromRuntime(prepDataDir, compiledPath, runId);
|
|
2288
2110
|
}
|
|
2289
2111
|
}
|
|
2290
|
-
refreshCompileRunFromRuntime(compiledPath, runId) {
|
|
2112
|
+
refreshCompileRunFromRuntime(prepDataDir, compiledPath, runId) {
|
|
2291
2113
|
const current = this.readCompileRun(compiledPath, runId);
|
|
2292
2114
|
if (!current)
|
|
2293
2115
|
return;
|
|
@@ -2334,9 +2156,9 @@ export class LocalServiceRuntime {
|
|
|
2334
2156
|
}),
|
|
2335
2157
|
};
|
|
2336
2158
|
next.latest_proof = [...next.stages].reverse().find((stage) => Boolean(stage.latest_proof))?.latest_proof ?? next.latest_proof;
|
|
2337
|
-
this.writeCompileRun(compiledPath, next);
|
|
2159
|
+
this.writeCompileRun(prepDataDir, compiledPath, next);
|
|
2338
2160
|
}
|
|
2339
|
-
async emitRuntimeDerivedEvents(compiledPath, runId) {
|
|
2161
|
+
async emitRuntimeDerivedEvents(prepDataDir, compiledPath, runId) {
|
|
2340
2162
|
const state = loadState(compiledPath);
|
|
2341
2163
|
const run = this.readCompileRun(compiledPath, runId);
|
|
2342
2164
|
if (!state?.stages || !run)
|
|
@@ -2347,7 +2169,7 @@ export class LocalServiceRuntime {
|
|
|
2347
2169
|
continue;
|
|
2348
2170
|
const artifacts = stageArtifactRefs(stage.stage_id, stageState.artifacts);
|
|
2349
2171
|
for (const artifact of artifacts) {
|
|
2350
|
-
await this.recordCompileRunEvent(compiledPath, runId, {
|
|
2172
|
+
await this.recordCompileRunEvent(prepDataDir, compiledPath, runId, {
|
|
2351
2173
|
type: "artifact.written",
|
|
2352
2174
|
event_id: createRunEventId("event"),
|
|
2353
2175
|
run_id: runId,
|
|
@@ -2356,7 +2178,7 @@ export class LocalServiceRuntime {
|
|
|
2356
2178
|
artifact,
|
|
2357
2179
|
});
|
|
2358
2180
|
}
|
|
2359
|
-
await this.recordCompileRunEvent(compiledPath, runId, {
|
|
2181
|
+
await this.recordCompileRunEvent(prepDataDir, compiledPath, runId, {
|
|
2360
2182
|
type: "proof.updated",
|
|
2361
2183
|
event_id: createRunEventId("event"),
|
|
2362
2184
|
run_id: runId,
|
|
@@ -2372,11 +2194,11 @@ export class LocalServiceRuntime {
|
|
|
2372
2194
|
});
|
|
2373
2195
|
}
|
|
2374
2196
|
}
|
|
2375
|
-
readLatestReadinessRun(preparationName) {
|
|
2376
|
-
return readSavedReadinessCheckRun(
|
|
2197
|
+
readLatestReadinessRun(prepDataDir, preparationName) {
|
|
2198
|
+
return readSavedReadinessCheckRun(prepDataDir, preparationName);
|
|
2377
2199
|
}
|
|
2378
2200
|
checksEvaluatedEvent(runId, readinessRun) {
|
|
2379
|
-
const target = readinessRun.compiled ?? readinessRun.
|
|
2201
|
+
const target = readinessRun.compiled ?? readinessRun.source_files;
|
|
2380
2202
|
return {
|
|
2381
2203
|
type: "checks.evaluated",
|
|
2382
2204
|
event_id: createRunEventId("event"),
|
|
@@ -2397,9 +2219,11 @@ export class LocalServiceRuntime {
|
|
|
2397
2219
|
readiness,
|
|
2398
2220
|
};
|
|
2399
2221
|
}
|
|
2400
|
-
|
|
2401
|
-
mkdirSync(
|
|
2402
|
-
writeJsonFile(
|
|
2222
|
+
writeVerifyRun(prepDataDir, compiledPath, run) {
|
|
2223
|
+
mkdirSync(verifyRunsRoot(compiledPath), { recursive: true });
|
|
2224
|
+
writeJsonFile(verifyRunPath(compiledPath, run.run_id), VerifyRunResourceSchema.parse(run));
|
|
2225
|
+
this.verifyRunCache.invalidatePreparation(prepDataDir, run.preparation);
|
|
2226
|
+
this.readinessCache.invalidatePreparation(prepDataDir, run.preparation);
|
|
2403
2227
|
}
|
|
2404
2228
|
}
|
|
2405
2229
|
export function createLocalServiceRuntime(options) {
|