@oscharko-dev/keiko 0.1.0-beta.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/LICENSE +202 -0
- package/NOTICE +7 -0
- package/README.md +621 -0
- package/TRADEMARKS.md +41 -0
- package/dist/audit/aggregate.d.ts +5 -0
- package/dist/audit/aggregate.js +25 -0
- package/dist/audit/build.d.ts +2 -0
- package/dist/audit/build.js +224 -0
- package/dist/audit/errors.d.ts +25 -0
- package/dist/audit/errors.js +39 -0
- package/dist/audit/index-api.d.ts +14 -0
- package/dist/audit/index-api.js +131 -0
- package/dist/audit/index.d.ts +12 -0
- package/dist/audit/index.js +17 -0
- package/dist/audit/persist.d.ts +8 -0
- package/dist/audit/persist.js +40 -0
- package/dist/audit/redaction.d.ts +3 -0
- package/dist/audit/redaction.js +61 -0
- package/dist/audit/report.d.ts +18 -0
- package/dist/audit/report.js +50 -0
- package/dist/audit/retention.d.ts +3 -0
- package/dist/audit/retention.js +95 -0
- package/dist/audit/runid.d.ts +1 -0
- package/dist/audit/runid.js +29 -0
- package/dist/audit/side-file.d.ts +12 -0
- package/dist/audit/side-file.js +82 -0
- package/dist/audit/store.d.ts +12 -0
- package/dist/audit/store.js +198 -0
- package/dist/audit/types.d.ts +188 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit/workflow-evidence.d.ts +27 -0
- package/dist/audit/workflow-evidence.js +145 -0
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/context.js +102 -0
- package/dist/cli/evaluate.d.ts +7 -0
- package/dist/cli/evaluate.js +207 -0
- package/dist/cli/evidence.d.ts +8 -0
- package/dist/cli/evidence.js +88 -0
- package/dist/cli/gateway-config.d.ts +10 -0
- package/dist/cli/gateway-config.js +12 -0
- package/dist/cli/gen-tests.d.ts +7 -0
- package/dist/cli/gen-tests.js +208 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/investigate.d.ts +8 -0
- package/dist/cli/investigate.js +242 -0
- package/dist/cli/models.d.ts +3 -0
- package/dist/cli/models.js +64 -0
- package/dist/cli/run.d.ts +7 -0
- package/dist/cli/run.js +187 -0
- package/dist/cli/runner.d.ts +6 -0
- package/dist/cli/runner.js +83 -0
- package/dist/cli/ui.d.ts +31 -0
- package/dist/cli/ui.js +240 -0
- package/dist/cli/verify.d.ts +2 -0
- package/dist/cli/verify.js +103 -0
- package/dist/evaluations/fixtures/bug-investigation/happy-path.d.ts +2 -0
- package/dist/evaluations/fixtures/bug-investigation/happy-path.js +66 -0
- package/dist/evaluations/fixtures/bug-investigation/investigation-only.d.ts +2 -0
- package/dist/evaluations/fixtures/bug-investigation/investigation-only.js +39 -0
- package/dist/evaluations/fixtures/bug-investigation/unsafe-action.d.ts +2 -0
- package/dist/evaluations/fixtures/bug-investigation/unsafe-action.js +37 -0
- package/dist/evaluations/fixtures/index.d.ts +7 -0
- package/dist/evaluations/fixtures/index.js +35 -0
- package/dist/evaluations/fixtures/support.d.ts +5 -0
- package/dist/evaluations/fixtures/support.js +42 -0
- package/dist/evaluations/fixtures/unit-tests/happy-path.d.ts +2 -0
- package/dist/evaluations/fixtures/unit-tests/happy-path.js +40 -0
- package/dist/evaluations/fixtures/unit-tests/retry-then-accept.d.ts +2 -0
- package/dist/evaluations/fixtures/unit-tests/retry-then-accept.js +39 -0
- package/dist/evaluations/fixtures/unit-tests/unsafe-action.d.ts +2 -0
- package/dist/evaluations/fixtures/unit-tests/unsafe-action.js +32 -0
- package/dist/evaluations/index.d.ts +12 -0
- package/dist/evaluations/index.js +12 -0
- package/dist/evaluations/manifest-check.d.ts +1 -0
- package/dist/evaluations/manifest-check.js +48 -0
- package/dist/evaluations/model-provider.d.ts +12 -0
- package/dist/evaluations/model-provider.js +26 -0
- package/dist/evaluations/render.d.ts +2 -0
- package/dist/evaluations/render.js +59 -0
- package/dist/evaluations/runner-support.d.ts +27 -0
- package/dist/evaluations/runner-support.js +163 -0
- package/dist/evaluations/runner.d.ts +20 -0
- package/dist/evaluations/runner.js +174 -0
- package/dist/evaluations/scorer.d.ts +14 -0
- package/dist/evaluations/scorer.js +131 -0
- package/dist/evaluations/scripted-model.d.ts +6 -0
- package/dist/evaluations/scripted-model.js +26 -0
- package/dist/evaluations/surface-parity.d.ts +2 -0
- package/dist/evaluations/surface-parity.js +184 -0
- package/dist/evaluations/types.d.ts +74 -0
- package/dist/evaluations/types.js +16 -0
- package/dist/gateway/capabilities.d.ts +11 -0
- package/dist/gateway/capabilities.data.d.ts +2 -0
- package/dist/gateway/capabilities.data.js +203 -0
- package/dist/gateway/capabilities.js +41 -0
- package/dist/gateway/config.d.ts +15 -0
- package/dist/gateway/config.js +154 -0
- package/dist/gateway/errors.d.ts +72 -0
- package/dist/gateway/errors.js +82 -0
- package/dist/gateway/gateway.d.ts +19 -0
- package/dist/gateway/gateway.js +94 -0
- package/dist/gateway/index.d.ts +10 -0
- package/dist/gateway/index.js +11 -0
- package/dist/gateway/model-selection.d.ts +9 -0
- package/dist/gateway/model-selection.js +36 -0
- package/dist/gateway/normalize.d.ts +7 -0
- package/dist/gateway/normalize.js +93 -0
- package/dist/gateway/openai-adapter.d.ts +20 -0
- package/dist/gateway/openai-adapter.js +263 -0
- package/dist/gateway/redaction.d.ts +1 -0
- package/dist/gateway/redaction.js +51 -0
- package/dist/gateway/resilience.d.ts +24 -0
- package/dist/gateway/resilience.js +166 -0
- package/dist/gateway/types.d.ts +108 -0
- package/dist/gateway/types.js +2 -0
- package/dist/harness/adapters.d.ts +23 -0
- package/dist/harness/adapters.js +38 -0
- package/dist/harness/context.d.ts +33 -0
- package/dist/harness/context.js +21 -0
- package/dist/harness/emitter.d.ts +15 -0
- package/dist/harness/emitter.js +72 -0
- package/dist/harness/errors.d.ts +21 -0
- package/dist/harness/errors.js +39 -0
- package/dist/harness/executor.d.ts +3 -0
- package/dist/harness/executor.js +211 -0
- package/dist/harness/fingerprint.d.ts +6 -0
- package/dist/harness/fingerprint.js +43 -0
- package/dist/harness/index.d.ts +9 -0
- package/dist/harness/index.js +13 -0
- package/dist/harness/loop.d.ts +3 -0
- package/dist/harness/loop.js +159 -0
- package/dist/harness/patcher.d.ts +4 -0
- package/dist/harness/patcher.js +49 -0
- package/dist/harness/planner.d.ts +3 -0
- package/dist/harness/planner.js +21 -0
- package/dist/harness/ports.d.ts +61 -0
- package/dist/harness/ports.js +4 -0
- package/dist/harness/session.d.ts +25 -0
- package/dist/harness/session.js +116 -0
- package/dist/harness/sinks.d.ts +30 -0
- package/dist/harness/sinks.js +72 -0
- package/dist/harness/tasks/explain-plan.d.ts +3 -0
- package/dist/harness/tasks/explain-plan.js +29 -0
- package/dist/harness/tasks/generate-unit-tests.d.ts +3 -0
- package/dist/harness/tasks/generate-unit-tests.js +28 -0
- package/dist/harness/tasks/investigate-bug.d.ts +3 -0
- package/dist/harness/tasks/investigate-bug.js +31 -0
- package/dist/harness/tasks/policy.d.ts +11 -0
- package/dist/harness/tasks/policy.js +22 -0
- package/dist/harness/tasks/verify.d.ts +3 -0
- package/dist/harness/tasks/verify.js +16 -0
- package/dist/harness/types.d.ts +270 -0
- package/dist/harness/types.js +33 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +36 -0
- package/dist/sdk/index.d.ts +9 -0
- package/dist/sdk/index.js +37 -0
- package/dist/sdk/run-agent.d.ts +16 -0
- package/dist/sdk/run-agent.js +56 -0
- package/dist/tools/browser/cdp-client.d.ts +35 -0
- package/dist/tools/browser/cdp-client.js +218 -0
- package/dist/tools/browser/errors.d.ts +25 -0
- package/dist/tools/browser/errors.js +55 -0
- package/dist/tools/browser/index.d.ts +5 -0
- package/dist/tools/browser/index.js +6 -0
- package/dist/tools/browser/session.d.ts +44 -0
- package/dist/tools/browser/session.js +748 -0
- package/dist/tools/browser/types.d.ts +48 -0
- package/dist/tools/browser/types.js +2 -0
- package/dist/tools/browser/validators.d.ts +5 -0
- package/dist/tools/browser/validators.js +97 -0
- package/dist/tools/errors.d.ts +59 -0
- package/dist/tools/errors.js +94 -0
- package/dist/tools/exec.d.ts +42 -0
- package/dist/tools/exec.js +327 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +14 -0
- package/dist/tools/patch-content.d.ts +10 -0
- package/dist/tools/patch-content.js +126 -0
- package/dist/tools/patch-normalize.d.ts +1 -0
- package/dist/tools/patch-normalize.js +80 -0
- package/dist/tools/patch-parse.d.ts +8 -0
- package/dist/tools/patch-parse.js +201 -0
- package/dist/tools/patch.d.ts +18 -0
- package/dist/tools/patch.js +403 -0
- package/dist/tools/registry.d.ts +36 -0
- package/dist/tools/registry.js +231 -0
- package/dist/tools/sandbox.d.ts +8 -0
- package/dist/tools/sandbox.js +121 -0
- package/dist/tools/schemas.d.ts +2 -0
- package/dist/tools/schemas.js +51 -0
- package/dist/tools/terminal-policy.d.ts +9 -0
- package/dist/tools/terminal-policy.js +313 -0
- package/dist/tools/types.d.ts +99 -0
- package/dist/tools/types.js +103 -0
- package/dist/tools/writer.d.ts +7 -0
- package/dist/tools/writer.js +20 -0
- package/dist/ui/browser.d.ts +10 -0
- package/dist/ui/browser.js +231 -0
- package/dist/ui/chat-handlers.d.ts +4 -0
- package/dist/ui/chat-handlers.js +281 -0
- package/dist/ui/csp-hashes.json +17 -0
- package/dist/ui/csp.d.ts +2 -0
- package/dist/ui/csp.js +66 -0
- package/dist/ui/deps.d.ts +34 -0
- package/dist/ui/deps.js +137 -0
- package/dist/ui/evidence.d.ts +27 -0
- package/dist/ui/evidence.js +142 -0
- package/dist/ui/files-deny.d.ts +2 -0
- package/dist/ui/files-deny.js +12 -0
- package/dist/ui/files.d.ts +65 -0
- package/dist/ui/files.js +492 -0
- package/dist/ui/headers.d.ts +2 -0
- package/dist/ui/headers.js +21 -0
- package/dist/ui/host-check.d.ts +2 -0
- package/dist/ui/host-check.js +58 -0
- package/dist/ui/index.d.ts +20 -0
- package/dist/ui/index.js +23 -0
- package/dist/ui/load-csp.d.ts +1 -0
- package/dist/ui/load-csp.js +28 -0
- package/dist/ui/read-handlers.d.ts +8 -0
- package/dist/ui/read-handlers.js +247 -0
- package/dist/ui/routes.d.ts +36 -0
- package/dist/ui/routes.js +129 -0
- package/dist/ui/run-engine.d.ts +20 -0
- package/dist/ui/run-engine.js +345 -0
- package/dist/ui/run-handlers.d.ts +8 -0
- package/dist/ui/run-handlers.js +431 -0
- package/dist/ui/run-request.d.ts +13 -0
- package/dist/ui/run-request.js +219 -0
- package/dist/ui/runs.d.ts +43 -0
- package/dist/ui/runs.js +92 -0
- package/dist/ui/server.d.ts +11 -0
- package/dist/ui/server.js +143 -0
- package/dist/ui/sink.d.ts +27 -0
- package/dist/ui/sink.js +80 -0
- package/dist/ui/sse.d.ts +7 -0
- package/dist/ui/sse.js +27 -0
- package/dist/ui/static/404.html +1 -0
- package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_buildManifest.js +1 -0
- package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_ssgManifest.js +1 -0
- package/dist/ui/static/_next/static/chunks/255-d47fd57964443afe.js +1 -0
- package/dist/ui/static/_next/static/chunks/4-be1fef693af8e088.js +1 -0
- package/dist/ui/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/_not-found/page-75825b09bcecad97.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/launch/page-9c86a13c29884245.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/layout-bdea63fe87947d50.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/page-4168c12c68b7a853.js +1 -0
- package/dist/ui/static/_next/static/chunks/framework-a6e0b7e30f98059a.js +1 -0
- package/dist/ui/static/_next/static/chunks/main-778a50aebff02192.js +1 -0
- package/dist/ui/static/_next/static/chunks/main-app-30679af7240d63e9.js +1 -0
- package/dist/ui/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/dist/ui/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/dist/ui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/ui/static/_next/static/chunks/webpack-4a462cecab786e93.js +1 -0
- package/dist/ui/static/_next/static/css/be7cb54d5c5673b6.css +1 -0
- package/dist/ui/static/assets/editors/goland.svg +35 -0
- package/dist/ui/static/assets/editors/intellij.svg +39 -0
- package/dist/ui/static/assets/editors/pycharm.svg +58 -0
- package/dist/ui/static/assets/editors/rustrover.svg +19 -0
- package/dist/ui/static/assets/editors/vscode.svg +1 -0
- package/dist/ui/static/assets/editors/webstorm.svg +21 -0
- package/dist/ui/static/assets/icons/anthropic.svg +1 -0
- package/dist/ui/static/assets/icons/brave.svg +1 -0
- package/dist/ui/static/assets/icons/css3.svg +1 -0
- package/dist/ui/static/assets/icons/docker.svg +1 -0
- package/dist/ui/static/assets/icons/git.svg +1 -0
- package/dist/ui/static/assets/icons/github.svg +1 -0
- package/dist/ui/static/assets/icons/go.svg +1 -0
- package/dist/ui/static/assets/icons/gradle.svg +1 -0
- package/dist/ui/static/assets/icons/grafana.svg +1 -0
- package/dist/ui/static/assets/icons/graphql.svg +1 -0
- package/dist/ui/static/assets/icons/html5.svg +1 -0
- package/dist/ui/static/assets/icons/image.svg +1 -0
- package/dist/ui/static/assets/icons/java.svg +1 -0
- package/dist/ui/static/assets/icons/javascript.svg +1 -0
- package/dist/ui/static/assets/icons/json.svg +1 -0
- package/dist/ui/static/assets/icons/kafka.svg +1 -0
- package/dist/ui/static/assets/icons/kubernetes.svg +1 -0
- package/dist/ui/static/assets/icons/linear.svg +1 -0
- package/dist/ui/static/assets/icons/markdown.svg +1 -0
- package/dist/ui/static/assets/icons/nginx.svg +1 -0
- package/dist/ui/static/assets/icons/nodejs.svg +1 -0
- package/dist/ui/static/assets/icons/notion.svg +1 -0
- package/dist/ui/static/assets/icons/openai.svg +1 -0
- package/dist/ui/static/assets/icons/playwright.svg +1 -0
- package/dist/ui/static/assets/icons/postgresql.svg +1 -0
- package/dist/ui/static/assets/icons/prometheus.svg +1 -0
- package/dist/ui/static/assets/icons/properties.svg +1 -0
- package/dist/ui/static/assets/icons/puppeteer.svg +1 -0
- package/dist/ui/static/assets/icons/python.svg +1 -0
- package/dist/ui/static/assets/icons/react.svg +1 -0
- package/dist/ui/static/assets/icons/redis.svg +1 -0
- package/dist/ui/static/assets/icons/rust.svg +1 -0
- package/dist/ui/static/assets/icons/sentry.svg +1 -0
- package/dist/ui/static/assets/icons/slack.svg +1 -0
- package/dist/ui/static/assets/icons/spring.svg +1 -0
- package/dist/ui/static/assets/icons/typescript.svg +1 -0
- package/dist/ui/static/assets/icons/upstash.svg +1 -0
- package/dist/ui/static/assets/icons/yaml.svg +1 -0
- package/dist/ui/static/assets/keiko-logo.svg +10 -0
- package/dist/ui/static/index.html +1 -0
- package/dist/ui/static/index.txt +19 -0
- package/dist/ui/static/keiko-logo.svg +10 -0
- package/dist/ui/static/launch.html +1 -0
- package/dist/ui/static/launch.txt +19 -0
- package/dist/ui/static.d.ts +3 -0
- package/dist/ui/static.js +72 -0
- package/dist/ui/store/chats.d.ts +14 -0
- package/dist/ui/store/chats.js +110 -0
- package/dist/ui/store/db.d.ts +6 -0
- package/dist/ui/store/db.js +182 -0
- package/dist/ui/store/errors.d.ts +12 -0
- package/dist/ui/store/errors.js +30 -0
- package/dist/ui/store/index.d.ts +6 -0
- package/dist/ui/store/index.js +6 -0
- package/dist/ui/store/messages.d.ts +5 -0
- package/dist/ui/store/messages.js +137 -0
- package/dist/ui/store/paths.d.ts +4 -0
- package/dist/ui/store/paths.js +69 -0
- package/dist/ui/store/projects.d.ts +7 -0
- package/dist/ui/store/projects.js +61 -0
- package/dist/ui/store/schema.d.ts +3 -0
- package/dist/ui/store/schema.js +77 -0
- package/dist/ui/store/types.d.ts +80 -0
- package/dist/ui/store/types.js +3 -0
- package/dist/ui/store/validation.d.ts +4 -0
- package/dist/ui/store/validation.js +72 -0
- package/dist/ui/store-handlers.d.ts +16 -0
- package/dist/ui/store-handlers.js +465 -0
- package/dist/ui/terminal-errors.d.ts +21 -0
- package/dist/ui/terminal-errors.js +45 -0
- package/dist/ui/terminal-evidence.d.ts +20 -0
- package/dist/ui/terminal-evidence.js +65 -0
- package/dist/ui/terminal-routes.d.ts +9 -0
- package/dist/ui/terminal-routes.js +219 -0
- package/dist/ui/terminal.d.ts +67 -0
- package/dist/ui/terminal.js +835 -0
- package/dist/verification/classify.d.ts +10 -0
- package/dist/verification/classify.js +53 -0
- package/dist/verification/detect.d.ts +4 -0
- package/dist/verification/detect.js +81 -0
- package/dist/verification/errors.d.ts +11 -0
- package/dist/verification/errors.js +21 -0
- package/dist/verification/index.d.ts +17 -0
- package/dist/verification/index.js +13 -0
- package/dist/verification/limits.d.ts +3 -0
- package/dist/verification/limits.js +40 -0
- package/dist/verification/monitor.d.ts +4 -0
- package/dist/verification/monitor.js +58 -0
- package/dist/verification/orchestrator.d.ts +16 -0
- package/dist/verification/orchestrator.js +363 -0
- package/dist/verification/plan.d.ts +9 -0
- package/dist/verification/plan.js +125 -0
- package/dist/verification/summary.d.ts +40 -0
- package/dist/verification/summary.js +67 -0
- package/dist/verification/types.d.ts +63 -0
- package/dist/verification/types.js +13 -0
- package/dist/workflows/bug-investigation/context.d.ts +7 -0
- package/dist/workflows/bug-investigation/context.js +119 -0
- package/dist/workflows/bug-investigation/descriptor.d.ts +3 -0
- package/dist/workflows/bug-investigation/descriptor.js +46 -0
- package/dist/workflows/bug-investigation/emit.d.ts +12 -0
- package/dist/workflows/bug-investigation/emit.js +35 -0
- package/dist/workflows/bug-investigation/events.d.ts +81 -0
- package/dist/workflows/bug-investigation/events.js +9 -0
- package/dist/workflows/bug-investigation/failure-parse.d.ts +3 -0
- package/dist/workflows/bug-investigation/failure-parse.js +154 -0
- package/dist/workflows/bug-investigation/guard.d.ts +2 -0
- package/dist/workflows/bug-investigation/guard.js +69 -0
- package/dist/workflows/bug-investigation/index.d.ts +7 -0
- package/dist/workflows/bug-investigation/index.js +13 -0
- package/dist/workflows/bug-investigation/internal.d.ts +37 -0
- package/dist/workflows/bug-investigation/internal.js +64 -0
- package/dist/workflows/bug-investigation/model-loop.d.ts +4 -0
- package/dist/workflows/bug-investigation/model-loop.js +223 -0
- package/dist/workflows/bug-investigation/parse.d.ts +3 -0
- package/dist/workflows/bug-investigation/parse.js +123 -0
- package/dist/workflows/bug-investigation/prompt.d.ts +4 -0
- package/dist/workflows/bug-investigation/prompt.js +107 -0
- package/dist/workflows/bug-investigation/report.d.ts +23 -0
- package/dist/workflows/bug-investigation/report.js +151 -0
- package/dist/workflows/bug-investigation/stages.d.ts +13 -0
- package/dist/workflows/bug-investigation/stages.js +242 -0
- package/dist/workflows/bug-investigation/types.d.ts +91 -0
- package/dist/workflows/bug-investigation/types.js +14 -0
- package/dist/workflows/bug-investigation/verify-stage.d.ts +10 -0
- package/dist/workflows/bug-investigation/verify-stage.js +91 -0
- package/dist/workflows/bug-investigation/workflow.d.ts +2 -0
- package/dist/workflows/bug-investigation/workflow.js +74 -0
- package/dist/workflows/descriptor.d.ts +20 -0
- package/dist/workflows/descriptor.js +8 -0
- package/dist/workflows/index.d.ts +3 -0
- package/dist/workflows/index.js +2 -0
- package/dist/workflows/unit-tests/context.d.ts +7 -0
- package/dist/workflows/unit-tests/context.js +129 -0
- package/dist/workflows/unit-tests/conventions.d.ts +4 -0
- package/dist/workflows/unit-tests/conventions.js +87 -0
- package/dist/workflows/unit-tests/descriptor.d.ts +4 -0
- package/dist/workflows/unit-tests/descriptor.js +43 -0
- package/dist/workflows/unit-tests/emit.d.ts +12 -0
- package/dist/workflows/unit-tests/emit.js +35 -0
- package/dist/workflows/unit-tests/events.d.ts +78 -0
- package/dist/workflows/unit-tests/events.js +7 -0
- package/dist/workflows/unit-tests/index.d.ts +6 -0
- package/dist/workflows/unit-tests/index.js +10 -0
- package/dist/workflows/unit-tests/internal.d.ts +35 -0
- package/dist/workflows/unit-tests/internal.js +43 -0
- package/dist/workflows/unit-tests/model-loop.d.ts +4 -0
- package/dist/workflows/unit-tests/model-loop.js +95 -0
- package/dist/workflows/unit-tests/parse.d.ts +6 -0
- package/dist/workflows/unit-tests/parse.js +68 -0
- package/dist/workflows/unit-tests/prompt.d.ts +4 -0
- package/dist/workflows/unit-tests/prompt.js +71 -0
- package/dist/workflows/unit-tests/report.d.ts +21 -0
- package/dist/workflows/unit-tests/report.js +90 -0
- package/dist/workflows/unit-tests/stages.d.ts +9 -0
- package/dist/workflows/unit-tests/stages.js +155 -0
- package/dist/workflows/unit-tests/types.d.ts +70 -0
- package/dist/workflows/unit-tests/types.js +11 -0
- package/dist/workflows/unit-tests/verify-stage.d.ts +9 -0
- package/dist/workflows/unit-tests/verify-stage.js +56 -0
- package/dist/workflows/unit-tests/workflow.d.ts +2 -0
- package/dist/workflows/unit-tests/workflow.js +58 -0
- package/dist/workspace/contextPack.d.ts +9 -0
- package/dist/workspace/contextPack.js +94 -0
- package/dist/workspace/detect.d.ts +3 -0
- package/dist/workspace/detect.js +135 -0
- package/dist/workspace/discovery.d.ts +9 -0
- package/dist/workspace/discovery.js +167 -0
- package/dist/workspace/errors.d.ts +39 -0
- package/dist/workspace/errors.js +66 -0
- package/dist/workspace/fs.d.ts +21 -0
- package/dist/workspace/fs.js +36 -0
- package/dist/workspace/ignore.d.ts +14 -0
- package/dist/workspace/ignore.js +176 -0
- package/dist/workspace/index.d.ts +11 -0
- package/dist/workspace/index.js +13 -0
- package/dist/workspace/paths.d.ts +2 -0
- package/dist/workspace/paths.js +38 -0
- package/dist/workspace/realpath.d.ts +7 -0
- package/dist/workspace/realpath.js +72 -0
- package/dist/workspace/retrieval.d.ts +9 -0
- package/dist/workspace/retrieval.js +74 -0
- package/dist/workspace/summary.d.ts +3 -0
- package/dist/workspace/summary.js +54 -0
- package/dist/workspace/types.d.ts +103 -0
- package/dist/workspace/types.js +27 -0
- package/package.json +58 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// The bounded model/validate/production-guard retry loop (ADR-0008 D6/D8). Each attempt builds the
|
|
2
|
+
// prompt, calls the injected ModelPort, parses the output, validates the diff through #6, and
|
|
3
|
+
// applies the production-code guard. The loop stops on the first accepted patch, after maxRetries
|
|
4
|
+
// rejections, or when model calls reach the maxModelCalls hard ceiling — whichever comes first. The
|
|
5
|
+
// model call is the one IO boundary here; its failure propagates to the workflow catch boundary.
|
|
6
|
+
import { nodeWorkspaceFs } from "../../workspace/fs.js";
|
|
7
|
+
import { validatePatch } from "../../tools/index.js";
|
|
8
|
+
import { isTestPath } from "./conventions.js";
|
|
9
|
+
import { parseModelOutput } from "./parse.js";
|
|
10
|
+
import { buildPrompt } from "./prompt.js";
|
|
11
|
+
// The production-code guard (D6): every changed path must satisfy isTestPath. Returns "out-of-scope"
|
|
12
|
+
// when any path is a non-test/traversal path, or undefined when all pass. The guard runs on
|
|
13
|
+
// validation.files[].path — the SAME string #6 resolves and would write.
|
|
14
|
+
function productionGuard(workspace, validation) {
|
|
15
|
+
const offending = validation.files.some((file) => !isTestPath(workspace, file.path));
|
|
16
|
+
return offending ? "out-of-scope" : undefined;
|
|
17
|
+
}
|
|
18
|
+
function emitValidation(state, validation, code) {
|
|
19
|
+
const ok = code === undefined && validation.ok;
|
|
20
|
+
state.emitter.emit({
|
|
21
|
+
type: "patch:validated",
|
|
22
|
+
ok,
|
|
23
|
+
patchBytes: validation.totalBytes,
|
|
24
|
+
filesChanged: validation.files.length,
|
|
25
|
+
...(ok ? {} : { rejectionCode: code ?? validation.reasons[0]?.code }),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function emptyPatchRejection(parsedDiff, validation) {
|
|
29
|
+
return parsedDiff.trim().length === 0 || validation.files.length === 0 ? "empty" : undefined;
|
|
30
|
+
}
|
|
31
|
+
async function callModel(state, messages, attempt, contextBytes) {
|
|
32
|
+
state.progress.modelCallCount = Math.max(state.progress.modelCallCount, attempt);
|
|
33
|
+
state.emitter.emit({ type: "workflow:model:call:started", attempt, contextBytes });
|
|
34
|
+
const response = await state.deps.model.call({ modelId: state.input.modelId, messages }, state.signal);
|
|
35
|
+
state.emitter.emit({
|
|
36
|
+
type: "workflow:model:call:completed",
|
|
37
|
+
attempt,
|
|
38
|
+
finishReason: response.finishReason,
|
|
39
|
+
promptTokens: response.usage.promptTokens,
|
|
40
|
+
completionTokens: response.usage.completionTokens,
|
|
41
|
+
latencyMs: response.usage.latencyMs,
|
|
42
|
+
});
|
|
43
|
+
return response.content;
|
|
44
|
+
}
|
|
45
|
+
// One attempt: prompt -> model -> parse -> validate -> guard.
|
|
46
|
+
async function attemptOnce(state, workspace, conventions, pack, attempt, rejectionReason) {
|
|
47
|
+
const messages = buildPrompt(state.input, conventions, pack, rejectionReason);
|
|
48
|
+
const content = await callModel(state, messages, attempt, pack.usedBytes);
|
|
49
|
+
const parsed = parseModelOutput(content);
|
|
50
|
+
const validation = validatePatch(workspace, parsed.diff, {
|
|
51
|
+
fs: state.deps.fs ?? nodeWorkspaceFs,
|
|
52
|
+
});
|
|
53
|
+
const effectiveDiff = validation.normalizedDiff ?? parsed.diff;
|
|
54
|
+
const guardCode = validation.ok
|
|
55
|
+
? emptyPatchRejection(effectiveDiff, validation) ?? productionGuard(workspace, validation)
|
|
56
|
+
: validation.reasons[0]?.code;
|
|
57
|
+
emitValidation(state, validation, guardCode);
|
|
58
|
+
if (validation.ok && guardCode === undefined) {
|
|
59
|
+
const accepted = {
|
|
60
|
+
diff: effectiveDiff,
|
|
61
|
+
validation,
|
|
62
|
+
coveredBehavior: parsed.coveredBehavior,
|
|
63
|
+
knownGaps: parsed.knownGaps,
|
|
64
|
+
};
|
|
65
|
+
return { accepted, rejectionCode: undefined };
|
|
66
|
+
}
|
|
67
|
+
return { accepted: undefined, rejectionCode: guardCode ?? "malformed" };
|
|
68
|
+
}
|
|
69
|
+
export async function runModelLoop(state, workspace, conventions, pack) {
|
|
70
|
+
let modelCallCount = 0;
|
|
71
|
+
let patchRetryCount = 0;
|
|
72
|
+
let rejectionReason;
|
|
73
|
+
while (modelCallCount < state.limits.maxModelCalls &&
|
|
74
|
+
patchRetryCount <= state.limits.maxRetries) {
|
|
75
|
+
modelCallCount += 1;
|
|
76
|
+
const result = await attemptOnce(state, workspace, conventions, pack, modelCallCount, rejectionReason);
|
|
77
|
+
if (result.accepted !== undefined) {
|
|
78
|
+
return {
|
|
79
|
+
accepted: result.accepted,
|
|
80
|
+
modelCallCount,
|
|
81
|
+
patchRetryCount,
|
|
82
|
+
lastRejectionCode: undefined,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
patchRetryCount += 1;
|
|
86
|
+
state.progress.patchRetryCount = patchRetryCount;
|
|
87
|
+
rejectionReason = result.rejectionCode;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
accepted: undefined,
|
|
91
|
+
modelCallCount,
|
|
92
|
+
patchRetryCount,
|
|
93
|
+
lastRejectionCode: rejectionReason,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Defensive parser for the model-output contract (ADR-0008 steering note B). The model is
|
|
2
|
+
// instructed (see prompt.ts) to emit the unified diff inside a fenced ```diff ... ``` block,
|
|
3
|
+
// optionally followed by labeled prose sections (## Covered behavior, ## Known gaps). This
|
|
4
|
+
// parser extracts those parts WITHOUT trusting the model to comply: if no fence is present the
|
|
5
|
+
// whole content is treated as the diff; prose sections are extracted only when their headings
|
|
6
|
+
// appear. All extraction uses plain string ops (line splitting, startsWith, trim) — zero regex,
|
|
7
|
+
// so there is no ReDoS surface (steering note F). Redaction happens at the workflow boundary,
|
|
8
|
+
// not here: this module is pure string parsing.
|
|
9
|
+
const FENCE = "```";
|
|
10
|
+
const COVERED_HEADING = "## covered behavior";
|
|
11
|
+
const GAPS_HEADING = "## known gaps";
|
|
12
|
+
// Returns the contents of the FIRST fenced block (```diff ... ``` or a bare ``` ... ```) and the
|
|
13
|
+
// text that follows the closing fence. When no fenced block is found, returns the whole input as
|
|
14
|
+
// the diff and an empty remainder (the no-fence fallback).
|
|
15
|
+
function extractFencedDiff(content) {
|
|
16
|
+
const lines = content.split("\n");
|
|
17
|
+
const openIndex = lines.findIndex((line) => line.trimStart().startsWith(FENCE));
|
|
18
|
+
if (openIndex === -1) {
|
|
19
|
+
return { diff: content.trim(), rest: "" };
|
|
20
|
+
}
|
|
21
|
+
const closeIndex = lines.findIndex((line, idx) => idx > openIndex && line.trimStart().startsWith(FENCE));
|
|
22
|
+
if (closeIndex === -1) {
|
|
23
|
+
// An opening fence with no close: treat everything after the opener as the diff.
|
|
24
|
+
return {
|
|
25
|
+
diff: lines
|
|
26
|
+
.slice(openIndex + 1)
|
|
27
|
+
.join("\n")
|
|
28
|
+
.trim(),
|
|
29
|
+
rest: "",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
diff: lines
|
|
34
|
+
.slice(openIndex + 1, closeIndex)
|
|
35
|
+
.join("\n")
|
|
36
|
+
.trim(),
|
|
37
|
+
rest: lines.slice(closeIndex + 1).join("\n"),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
// Extracts the body of a labeled section: every line after the matching `## heading` up to the
|
|
41
|
+
// next `## ` heading (or end of input). Returns undefined when the heading is absent. Returns
|
|
42
|
+
// undefined when the section body is empty after trimming.
|
|
43
|
+
function extractSection(text, heading) {
|
|
44
|
+
const lines = text.split("\n");
|
|
45
|
+
const start = lines.findIndex((line) => line.trim().toLowerCase() === heading);
|
|
46
|
+
if (start === -1) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
const body = [];
|
|
50
|
+
for (const line of lines.slice(start + 1)) {
|
|
51
|
+
if (line.trim().startsWith("## ")) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
body.push(line);
|
|
55
|
+
}
|
|
56
|
+
const joined = body.join("\n").trim();
|
|
57
|
+
return joined.length === 0 ? undefined : joined;
|
|
58
|
+
}
|
|
59
|
+
export function parseModelOutput(content) {
|
|
60
|
+
const { diff, rest } = extractFencedDiff(content);
|
|
61
|
+
// Prose may follow the fenced diff; when there is no fence the whole content is the diff and
|
|
62
|
+
// there is no prose to scan, so `rest` is empty and both sections resolve to undefined.
|
|
63
|
+
return {
|
|
64
|
+
diff,
|
|
65
|
+
coveredBehavior: extractSection(rest, COVERED_HEADING),
|
|
66
|
+
knownGaps: extractSection(rest, GAPS_HEADING),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ChatMessage } from "../../gateway/types.js";
|
|
2
|
+
import type { ContextPack } from "../../workspace/index.js";
|
|
3
|
+
import type { TestConventions, UnitTestWorkflowInput } from "./types.js";
|
|
4
|
+
export declare function buildPrompt(input: UnitTestWorkflowInput, conventions: TestConventions, pack: ContextPack, rejectionReason?: string): readonly ChatMessage[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Prompt construction (ADR-0008 D1/D7). Builds the system + user ChatMessage array for the single
|
|
2
|
+
// generation call. PURE: no IO, no clock, no randomness — the same inputs always yield the same
|
|
3
|
+
// messages. The system message specifies the model-output contract (a fenced ```diff block plus
|
|
4
|
+
// optional labeled prose) that parse.ts consumes (steering note B). Context excerpts handed in are
|
|
5
|
+
// already redacted by #5; this module does no redaction.
|
|
6
|
+
const OUTPUT_CONTRACT = "Respond with the unified diff that adds the tests inside a single fenced code block opened " +
|
|
7
|
+
"with ```diff and closed with ```. After the block you MAY add `## Covered behavior` and " +
|
|
8
|
+
"`## Known gaps` sections in prose. Output ONLY a unified diff for test files — never modify " +
|
|
9
|
+
"source files. The first non-empty line inside the fence MUST be `--- /dev/null` for a new " +
|
|
10
|
+
"test file or `--- a/<existing-test-path>` for an existing test file, followed by " +
|
|
11
|
+
"`+++ b/<test-path>` and at least one `@@` hunk. Do not output `*** Begin Patch`, file trees, " +
|
|
12
|
+
"plain prose, markdown bullets, or escaped newline markers like `\\n+`/`\\n-` inside the diff " +
|
|
13
|
+
"fence; every diff line must be separated by a real newline.";
|
|
14
|
+
function systemContent(conventions) {
|
|
15
|
+
const lines = [
|
|
16
|
+
"You are a senior engineer writing rigorous, deterministic unit tests for existing code.",
|
|
17
|
+
`Test framework: ${conventions.framework}.`,
|
|
18
|
+
`Place new tests using the project's "${conventions.fileNamingStyle}" naming convention.`,
|
|
19
|
+
conventions.testDirs.length > 0
|
|
20
|
+
? `Project test directories: ${conventions.testDirs.join(", ")}.`
|
|
21
|
+
: "No dedicated test directory detected; place tests beside their source.",
|
|
22
|
+
"Cover edge cases explicitly: null, undefined, empty, zero, boundary, negative, and error paths.",
|
|
23
|
+
OUTPUT_CONTRACT,
|
|
24
|
+
];
|
|
25
|
+
return `${lines.join("\n")}${assertionStyleBlock(conventions)}`;
|
|
26
|
+
}
|
|
27
|
+
function assertionStyleBlock(conventions) {
|
|
28
|
+
if (conventions.assertionStyleSamples.length === 0) {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
const samples = conventions.assertionStyleSamples
|
|
32
|
+
.map((sample, idx) => `Example test ${String(idx + 1)}:\n${sample}`)
|
|
33
|
+
.join("\n\n");
|
|
34
|
+
return `\n\nMatch the assertion and structure style of these existing tests:\n${samples}`;
|
|
35
|
+
}
|
|
36
|
+
function targetDescription(target) {
|
|
37
|
+
if (target.kind === "file") {
|
|
38
|
+
return target.targetFunction === undefined
|
|
39
|
+
? `Write unit tests for the public API in ${target.filePath}.`
|
|
40
|
+
: `Write unit tests for the function ${target.targetFunction} in ${target.filePath}.`;
|
|
41
|
+
}
|
|
42
|
+
if (target.kind === "module") {
|
|
43
|
+
return `Write unit tests for the source files in the module directory ${target.moduleDir}.`;
|
|
44
|
+
}
|
|
45
|
+
return `Write unit tests for these changed files: ${target.filePaths.join(", ")}.`;
|
|
46
|
+
}
|
|
47
|
+
function contextBlock(pack) {
|
|
48
|
+
if (pack.selected.length === 0) {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
const entries = pack.selected
|
|
52
|
+
.map((entry) => `--- ${entry.path} ---\n${entry.excerpt}`)
|
|
53
|
+
.join("\n\n");
|
|
54
|
+
return `\n\nRepository context:\n${entries}`;
|
|
55
|
+
}
|
|
56
|
+
function userContent(input, pack, rejectionReason) {
|
|
57
|
+
const retry = rejectionReason === undefined
|
|
58
|
+
? ""
|
|
59
|
+
: `\n\nThe previous diff was rejected: ${rejectionReason}. Produce a corrected diff that ` +
|
|
60
|
+
"modifies ONLY test files.";
|
|
61
|
+
return `${targetDescription(input.target)}${contextBlock(pack)}${retry}`;
|
|
62
|
+
}
|
|
63
|
+
// rejectionReason is appended on a retry (D8) so the model can correct an invalid/out-of-scope
|
|
64
|
+
// diff; it is undefined on the first attempt. The documented core signature is (input, conventions,
|
|
65
|
+
// pack); the optional 4th argument carries retry state without breaking that contract.
|
|
66
|
+
export function buildPrompt(input, conventions, pack, rejectionReason) {
|
|
67
|
+
return [
|
|
68
|
+
{ role: "system", content: systemContent(conventions) },
|
|
69
|
+
{ role: "user", content: userContent(input, pack, rejectionReason) },
|
|
70
|
+
];
|
|
71
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PatchFileChange } from "../../tools/index.js";
|
|
2
|
+
import type { VerificationAuditSummary } from "../../verification/index.js";
|
|
3
|
+
import type { UnitTestWorkflowReport, WorkflowStatus } from "./types.js";
|
|
4
|
+
export interface ReportParts {
|
|
5
|
+
readonly status: WorkflowStatus;
|
|
6
|
+
readonly modelId: string;
|
|
7
|
+
readonly durationMs: number;
|
|
8
|
+
readonly patchFiles: readonly PatchFileChange[];
|
|
9
|
+
readonly dryRunPreview: string | undefined;
|
|
10
|
+
readonly proposedDiff: string | undefined;
|
|
11
|
+
readonly coveredBehavior: string | undefined;
|
|
12
|
+
readonly knownGaps: string | undefined;
|
|
13
|
+
readonly nextActions: readonly string[];
|
|
14
|
+
readonly failureReason?: string | undefined;
|
|
15
|
+
readonly verificationSummary: VerificationAuditSummary | undefined;
|
|
16
|
+
readonly verificationSkipReason: string | undefined;
|
|
17
|
+
readonly modelCallCount: number;
|
|
18
|
+
readonly patchRetryCount: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function assembleReport(parts: ReportParts): UnitTestWorkflowReport;
|
|
21
|
+
export declare function renderMarkdownReport(report: UnitTestWorkflowReport): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Report assembly and Markdown rendering (ADR-0008 D3, steering note A). assembleReport composes
|
|
2
|
+
// the JSON-serializable UnitTestWorkflowReport from pipeline stage outputs; renderMarkdownReport
|
|
3
|
+
// produces the CLI text path. ALL prose (coveredBehavior, knownGaps, nextActions), the dry-run
|
|
4
|
+
// preview, the proposed diff, verificationSkipReason, and addedTestFiles[].path are redacted via
|
|
5
|
+
// redact() here so nothing the report carries can leak a secret — defence in depth on top of the
|
|
6
|
+
// redaction already applied upstream. Pure: no IO, no clock; the caller injects durationMs and counters.
|
|
7
|
+
import { redact } from "../../gateway/redaction.js";
|
|
8
|
+
const TEST_CASE_PREFIXES = ["test(", "it(", "describe("];
|
|
9
|
+
// Best-effort count of added test cases: added (`+`) lines whose trimmed text begins with a known
|
|
10
|
+
// test-case opener. Not authoritative — purely informational for the report.
|
|
11
|
+
function estimateTestCount(file) {
|
|
12
|
+
let count = 0;
|
|
13
|
+
for (const hunk of file.hunks) {
|
|
14
|
+
for (const line of hunk.lines) {
|
|
15
|
+
if (!line.startsWith("+")) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const trimmed = line.slice(1).trimStart();
|
|
19
|
+
if (TEST_CASE_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
|
|
20
|
+
count += 1;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return count;
|
|
25
|
+
}
|
|
26
|
+
function toAddedTestFiles(files) {
|
|
27
|
+
return files.map((file) => ({
|
|
28
|
+
path: redact(file.path),
|
|
29
|
+
estimatedTestCount: estimateTestCount(file),
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
function redactOptional(value) {
|
|
33
|
+
return value === undefined ? undefined : redact(value);
|
|
34
|
+
}
|
|
35
|
+
export function assembleReport(parts) {
|
|
36
|
+
return {
|
|
37
|
+
workflowId: "unit-test-generation",
|
|
38
|
+
status: parts.status,
|
|
39
|
+
modelId: parts.modelId,
|
|
40
|
+
durationMs: parts.durationMs,
|
|
41
|
+
dryRunPreview: redactOptional(parts.dryRunPreview),
|
|
42
|
+
proposedDiff: redactOptional(parts.proposedDiff),
|
|
43
|
+
addedTestFiles: toAddedTestFiles(parts.patchFiles),
|
|
44
|
+
coveredBehavior: redactOptional(parts.coveredBehavior),
|
|
45
|
+
knownGaps: redactOptional(parts.knownGaps),
|
|
46
|
+
nextActions: parts.nextActions.map((action) => redact(action)),
|
|
47
|
+
failureReason: redactOptional(parts.failureReason),
|
|
48
|
+
verificationSummary: parts.verificationSummary,
|
|
49
|
+
verificationSkipReason: redactOptional(parts.verificationSkipReason),
|
|
50
|
+
modelCallCount: parts.modelCallCount,
|
|
51
|
+
patchRetryCount: parts.patchRetryCount,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function sectionIf(heading, body) {
|
|
55
|
+
return body === undefined ? [] : [`## ${heading}`, body, ""];
|
|
56
|
+
}
|
|
57
|
+
function fileLines(report) {
|
|
58
|
+
if (report.addedTestFiles.length === 0) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const rows = report.addedTestFiles.map((f) => `- ${f.path} (~${String(f.estimatedTestCount)} test case(s))`);
|
|
62
|
+
return ["## Test files", ...rows, ""];
|
|
63
|
+
}
|
|
64
|
+
function verificationLine(report) {
|
|
65
|
+
if (report.verificationSummary !== undefined) {
|
|
66
|
+
return [`## Verification`, `Status: ${report.verificationSummary.overallStatus}`, ""];
|
|
67
|
+
}
|
|
68
|
+
if (report.verificationSkipReason !== undefined) {
|
|
69
|
+
return [`## Verification`, report.verificationSkipReason, ""];
|
|
70
|
+
}
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
// A human-readable Markdown report for the CLI text path. Every field is already redacted by
|
|
74
|
+
// assembleReport, so rendering is plain string composition.
|
|
75
|
+
export function renderMarkdownReport(report) {
|
|
76
|
+
return [
|
|
77
|
+
`# Unit-test generation: ${report.status}`,
|
|
78
|
+
`Model: ${report.modelId} · ${String(report.durationMs)}ms · ` +
|
|
79
|
+
`${String(report.modelCallCount)} model call(s) · ${String(report.patchRetryCount)} retry(ies)`,
|
|
80
|
+
"",
|
|
81
|
+
...fileLines(report),
|
|
82
|
+
...sectionIf("Covered behavior", report.coveredBehavior),
|
|
83
|
+
...sectionIf("Known gaps", report.knownGaps),
|
|
84
|
+
...sectionIf("Failure", report.failureReason),
|
|
85
|
+
...verificationLine(report),
|
|
86
|
+
...(report.nextActions.length > 0
|
|
87
|
+
? ["## Next actions", ...report.nextActions.map((a) => `- ${a}`), ""]
|
|
88
|
+
: []),
|
|
89
|
+
].join("\n");
|
|
90
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { WorkspaceInfo } from "../../workspace/index.js";
|
|
2
|
+
import { type AcceptedPatch, type ModelLoopResult, type RunState } from "./internal.js";
|
|
3
|
+
import type { UnitTestWorkflowReport } from "./types.js";
|
|
4
|
+
export declare function rejectedReport(state: RunState, loop: ModelLoopResult): UnitTestWorkflowReport;
|
|
5
|
+
export declare function dryRunReport(state: RunState, loop: ModelLoopResult, accepted: AcceptedPatch): UnitTestWorkflowReport;
|
|
6
|
+
export declare function cancelledReport(state: RunState, loop: ModelLoopResult, accepted: AcceptedPatch | undefined): UnitTestWorkflowReport;
|
|
7
|
+
export declare function failedReport(state: RunState, error: unknown): UnitTestWorkflowReport;
|
|
8
|
+
export declare function emitCompleted(state: RunState, report: UnitTestWorkflowReport): UnitTestWorkflowReport;
|
|
9
|
+
export declare function finishPipeline(state: RunState, workspace: WorkspaceInfo, loop: ModelLoopResult): Promise<UnitTestWorkflowReport>;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// Terminal-report stages (ADR-0008 D3/D5). Each function maps a pipeline outcome — rejected,
|
|
2
|
+
// dry-run, applied+verified, cancelled, or failed — to the redacted UnitTestWorkflowReport via
|
|
3
|
+
// assembleReport. applyPatch (apply mode) is the one IO boundary here and is fail-closed in #6
|
|
4
|
+
// (applyEnabled gates the write). finishPipeline selects the branch; emitCompleted stamps the
|
|
5
|
+
// terminal event. All prose/diff redaction happens inside assembleReport.
|
|
6
|
+
import { redact } from "../../gateway/redaction.js";
|
|
7
|
+
import { applyPatch, CommandCancelledError, renderDryRun, } from "../../tools/index.js";
|
|
8
|
+
import { nodeWorkspaceFs } from "../../workspace/fs.js";
|
|
9
|
+
import { assembleReport } from "./report.js";
|
|
10
|
+
import { runWorkflowVerification } from "./verify-stage.js";
|
|
11
|
+
import { nextActionsFor, } from "./internal.js";
|
|
12
|
+
export function rejectedReport(state, loop) {
|
|
13
|
+
return assembleReport({
|
|
14
|
+
status: "rejected",
|
|
15
|
+
modelId: state.input.modelId,
|
|
16
|
+
durationMs: state.now() - state.startedAt,
|
|
17
|
+
patchFiles: [],
|
|
18
|
+
dryRunPreview: undefined,
|
|
19
|
+
proposedDiff: undefined,
|
|
20
|
+
coveredBehavior: undefined,
|
|
21
|
+
knownGaps: undefined,
|
|
22
|
+
nextActions: [
|
|
23
|
+
`The model did not produce an in-scope test patch (${loop.lastRejectionCode ?? "unknown"})`,
|
|
24
|
+
],
|
|
25
|
+
failureReason: undefined,
|
|
26
|
+
verificationSummary: undefined,
|
|
27
|
+
verificationSkipReason: undefined,
|
|
28
|
+
modelCallCount: loop.modelCallCount,
|
|
29
|
+
patchRetryCount: loop.patchRetryCount,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function dryRunReport(state, loop, accepted) {
|
|
33
|
+
const files = accepted.validation.files.map((f) => f.path);
|
|
34
|
+
return assembleReport({
|
|
35
|
+
status: "dry-run",
|
|
36
|
+
modelId: state.input.modelId,
|
|
37
|
+
durationMs: state.now() - state.startedAt,
|
|
38
|
+
patchFiles: accepted.validation.files,
|
|
39
|
+
dryRunPreview: renderDryRun(accepted.validation),
|
|
40
|
+
proposedDiff: accepted.diff,
|
|
41
|
+
coveredBehavior: accepted.coveredBehavior,
|
|
42
|
+
knownGaps: accepted.knownGaps,
|
|
43
|
+
nextActions: nextActionsFor(false, files),
|
|
44
|
+
failureReason: undefined,
|
|
45
|
+
verificationSummary: undefined,
|
|
46
|
+
verificationSkipReason: "verification skipped: dry-run, no files written",
|
|
47
|
+
modelCallCount: loop.modelCallCount,
|
|
48
|
+
patchRetryCount: loop.patchRetryCount,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function cancelledReport(state, loop, accepted) {
|
|
52
|
+
return assembleReport({
|
|
53
|
+
status: "cancelled",
|
|
54
|
+
modelId: state.input.modelId,
|
|
55
|
+
durationMs: state.now() - state.startedAt,
|
|
56
|
+
patchFiles: accepted?.validation.files ?? [],
|
|
57
|
+
dryRunPreview: accepted === undefined ? undefined : renderDryRun(accepted.validation),
|
|
58
|
+
proposedDiff: accepted?.diff,
|
|
59
|
+
coveredBehavior: undefined,
|
|
60
|
+
knownGaps: undefined,
|
|
61
|
+
nextActions: ["The workflow was cancelled before completion"],
|
|
62
|
+
failureReason: undefined,
|
|
63
|
+
verificationSummary: undefined,
|
|
64
|
+
verificationSkipReason: "verification skipped: cancelled",
|
|
65
|
+
modelCallCount: loop.modelCallCount,
|
|
66
|
+
patchRetryCount: loop.patchRetryCount,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export function failedReport(state, error) {
|
|
70
|
+
const message = redact(error instanceof Error ? error.message : "unexpected workflow failure");
|
|
71
|
+
const errorCode = error instanceof Error ? error.name : "UNKNOWN";
|
|
72
|
+
state.emitter.emit({ type: "workflow:failed", errorCode, message });
|
|
73
|
+
return assembleReport({
|
|
74
|
+
status: "failed",
|
|
75
|
+
modelId: state.input.modelId,
|
|
76
|
+
durationMs: state.now() - state.startedAt,
|
|
77
|
+
patchFiles: [],
|
|
78
|
+
dryRunPreview: undefined,
|
|
79
|
+
proposedDiff: undefined,
|
|
80
|
+
coveredBehavior: undefined,
|
|
81
|
+
knownGaps: undefined,
|
|
82
|
+
nextActions: [`Inspect the error and retry: ${message}`],
|
|
83
|
+
failureReason: message,
|
|
84
|
+
verificationSummary: undefined,
|
|
85
|
+
verificationSkipReason: undefined,
|
|
86
|
+
modelCallCount: state.progress.modelCallCount,
|
|
87
|
+
patchRetryCount: state.progress.patchRetryCount,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async function applyAndVerify(state, workspace, loop, accepted) {
|
|
91
|
+
const fs = state.deps.fs ?? nodeWorkspaceFs;
|
|
92
|
+
let applyResult;
|
|
93
|
+
try {
|
|
94
|
+
applyResult = applyPatch(workspace, accepted.diff, {
|
|
95
|
+
applyEnabled: true,
|
|
96
|
+
signal: state.signal,
|
|
97
|
+
fs,
|
|
98
|
+
...(state.deps.writer === undefined ? {} : { writer: state.deps.writer }),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
if (error instanceof CommandCancelledError) {
|
|
103
|
+
return cancelledReport(state, loop, accepted);
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
state.emitter.emit({
|
|
108
|
+
type: "workflow:patch:applied",
|
|
109
|
+
changedFiles: applyResult.changedFiles.length,
|
|
110
|
+
created: applyResult.created.length,
|
|
111
|
+
deleted: applyResult.deleted.length,
|
|
112
|
+
});
|
|
113
|
+
if (state.signal.aborted) {
|
|
114
|
+
return cancelledReport(state, loop, accepted);
|
|
115
|
+
}
|
|
116
|
+
const verification = await runWorkflowVerification(state, workspace, fs);
|
|
117
|
+
return assembleReport({
|
|
118
|
+
status: "completed",
|
|
119
|
+
modelId: state.input.modelId,
|
|
120
|
+
durationMs: state.now() - state.startedAt,
|
|
121
|
+
patchFiles: accepted.validation.files,
|
|
122
|
+
dryRunPreview: renderDryRun(accepted.validation),
|
|
123
|
+
proposedDiff: accepted.diff,
|
|
124
|
+
coveredBehavior: accepted.coveredBehavior,
|
|
125
|
+
knownGaps: accepted.knownGaps,
|
|
126
|
+
nextActions: nextActionsFor(true, applyResult.changedFiles),
|
|
127
|
+
failureReason: undefined,
|
|
128
|
+
verificationSummary: verification.summary,
|
|
129
|
+
verificationSkipReason: verification.skipReason,
|
|
130
|
+
modelCallCount: loop.modelCallCount,
|
|
131
|
+
patchRetryCount: loop.patchRetryCount,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
export function emitCompleted(state, report) {
|
|
135
|
+
state.emitter.emit({
|
|
136
|
+
type: "workflow:completed",
|
|
137
|
+
status: report.status,
|
|
138
|
+
durationMs: report.durationMs,
|
|
139
|
+
});
|
|
140
|
+
return report;
|
|
141
|
+
}
|
|
142
|
+
// Selects the terminal branch from the model-loop result: rejected (no patch), cancelled (abort
|
|
143
|
+
// before apply), apply+verify (apply mode), or dry-run (default).
|
|
144
|
+
export async function finishPipeline(state, workspace, loop) {
|
|
145
|
+
if (loop.accepted === undefined) {
|
|
146
|
+
return rejectedReport(state, loop);
|
|
147
|
+
}
|
|
148
|
+
if (state.signal.aborted) {
|
|
149
|
+
return cancelledReport(state, loop, loop.accepted);
|
|
150
|
+
}
|
|
151
|
+
if (state.input.apply === true) {
|
|
152
|
+
return applyAndVerify(state, workspace, loop, loop.accepted);
|
|
153
|
+
}
|
|
154
|
+
return dryRunReport(state, loop, loop.accepted);
|
|
155
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { ModelPort } from "../../harness/ports.js";
|
|
2
|
+
import type { SpawnFn, WorkspaceWriter } from "../../tools/index.js";
|
|
3
|
+
import type { TestFramework, WorkspaceFs } from "../../workspace/index.js";
|
|
4
|
+
import type { VerificationAuditSummary } from "../../verification/index.js";
|
|
5
|
+
import type { WorkflowEventSink } from "./events.js";
|
|
6
|
+
export type WorkflowStatus = "completed" | "dry-run" | "rejected" | "cancelled" | "failed";
|
|
7
|
+
export type UnitTestTarget = {
|
|
8
|
+
readonly kind: "file";
|
|
9
|
+
readonly filePath: string;
|
|
10
|
+
readonly targetFunction?: string | undefined;
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: "module";
|
|
13
|
+
readonly moduleDir: string;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "changedFiles";
|
|
16
|
+
readonly filePaths: readonly string[];
|
|
17
|
+
};
|
|
18
|
+
export type FileNamingStyle = "sibling" | "mirrored" | "unknown";
|
|
19
|
+
export interface TestConventions {
|
|
20
|
+
readonly framework: TestFramework;
|
|
21
|
+
readonly testDirs: readonly string[];
|
|
22
|
+
readonly fileNamingStyle: FileNamingStyle;
|
|
23
|
+
readonly assertionStyleSamples: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export interface WorkflowLimits {
|
|
26
|
+
readonly maxModelCalls: number;
|
|
27
|
+
readonly maxRetries: number;
|
|
28
|
+
readonly contextBudgetBytes: number;
|
|
29
|
+
readonly maxBytesPerFile: number;
|
|
30
|
+
}
|
|
31
|
+
export declare const DEFAULT_WORKFLOW_LIMITS: WorkflowLimits;
|
|
32
|
+
export interface UnitTestWorkflowInput {
|
|
33
|
+
readonly workspaceRoot: string;
|
|
34
|
+
readonly target: UnitTestTarget;
|
|
35
|
+
readonly apply?: boolean | undefined;
|
|
36
|
+
readonly modelId: string;
|
|
37
|
+
readonly limits?: Partial<WorkflowLimits> | undefined;
|
|
38
|
+
}
|
|
39
|
+
export interface UnitTestWorkflowDeps {
|
|
40
|
+
readonly model: ModelPort;
|
|
41
|
+
readonly fs?: WorkspaceFs | undefined;
|
|
42
|
+
readonly writer?: WorkspaceWriter | undefined;
|
|
43
|
+
readonly spawn?: SpawnFn | undefined;
|
|
44
|
+
readonly now?: (() => number) | undefined;
|
|
45
|
+
readonly idSource?: (() => string) | undefined;
|
|
46
|
+
readonly sink?: WorkflowEventSink | undefined;
|
|
47
|
+
readonly processEnv?: NodeJS.ProcessEnv | undefined;
|
|
48
|
+
readonly signal?: AbortSignal | undefined;
|
|
49
|
+
}
|
|
50
|
+
export interface AddedTestFile {
|
|
51
|
+
readonly path: string;
|
|
52
|
+
readonly estimatedTestCount: number;
|
|
53
|
+
}
|
|
54
|
+
export interface UnitTestWorkflowReport {
|
|
55
|
+
readonly workflowId: "unit-test-generation";
|
|
56
|
+
readonly status: WorkflowStatus;
|
|
57
|
+
readonly modelId: string;
|
|
58
|
+
readonly durationMs: number;
|
|
59
|
+
readonly dryRunPreview?: string | undefined;
|
|
60
|
+
readonly proposedDiff?: string | undefined;
|
|
61
|
+
readonly addedTestFiles: readonly AddedTestFile[];
|
|
62
|
+
readonly coveredBehavior?: string | undefined;
|
|
63
|
+
readonly knownGaps?: string | undefined;
|
|
64
|
+
readonly nextActions: readonly string[];
|
|
65
|
+
readonly failureReason?: string | undefined;
|
|
66
|
+
readonly verificationSummary?: VerificationAuditSummary | undefined;
|
|
67
|
+
readonly verificationSkipReason?: string | undefined;
|
|
68
|
+
readonly modelCallCount: number;
|
|
69
|
+
readonly patchRetryCount: number;
|
|
70
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// All interfaces, type aliases, and frozen constant tables for the unit-test generation
|
|
2
|
+
// workflow (ADR-0008 D2/D3/D7). No runtime logic lives here beyond the frozen tables the type
|
|
3
|
+
// layer exposes as values, mirroring the ADR-0003/0004/0005/0006/0007 `types.ts` precedent.
|
|
4
|
+
// `readonly` everywhere; optional props are `| undefined` because exactOptionalPropertyTypes
|
|
5
|
+
// is on. Every report shape is plain JSON-serializable so the #10 audit ledger can persist it.
|
|
6
|
+
export const DEFAULT_WORKFLOW_LIMITS = {
|
|
7
|
+
maxModelCalls: 3,
|
|
8
|
+
maxRetries: 2,
|
|
9
|
+
contextBudgetBytes: 65_536,
|
|
10
|
+
maxBytesPerFile: 8_192,
|
|
11
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type WorkspaceFs, type WorkspaceInfo } from "../../workspace/index.js";
|
|
2
|
+
import { type VerificationAuditSummary } from "../../verification/index.js";
|
|
3
|
+
import type { RunState } from "./internal.js";
|
|
4
|
+
export declare const SKIP_UNRESOLVED = "verification skipped: framework unknown or no test files resolved";
|
|
5
|
+
export interface VerificationOutcome {
|
|
6
|
+
readonly summary: VerificationAuditSummary | undefined;
|
|
7
|
+
readonly skipReason: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
export declare function runWorkflowVerification(state: RunState, workspace: WorkspaceInfo, fs: WorkspaceFs): Promise<VerificationOutcome>;
|