@provos/ironcurtain 0.9.1 → 0.11.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 +69 -10
- package/dist/auth/oauth-provider.js +1 -4
- package/dist/auth/oauth-provider.js.map +1 -1
- package/dist/cli.js +40 -2
- package/dist/cli.js.map +1 -1
- package/dist/config/config-command.js +174 -10
- package/dist/config/config-command.js.map +1 -1
- package/dist/config/first-start.js +61 -0
- package/dist/config/first-start.js.map +1 -1
- package/dist/config/generated/compiled-policy.json +528 -161
- package/dist/config/generated/test-scenarios.json +4810 -170
- package/dist/config/generated/tool-annotations.json +34 -47
- package/dist/config/index.d.ts +58 -0
- package/dist/config/index.js +97 -15
- package/dist/config/index.js.map +1 -1
- package/dist/config/mcp-servers.json +2 -2
- package/dist/config/model-provider.d.ts +8 -2
- package/dist/config/model-provider.js +52 -9
- package/dist/config/model-provider.js.map +1 -1
- package/dist/config/paths.d.ts +181 -0
- package/dist/config/paths.js +326 -23
- package/dist/config/paths.js.map +1 -1
- package/dist/config/types.d.ts +7 -1
- package/dist/config/user-config.d.ts +61 -4
- package/dist/config/user-config.js +108 -21
- package/dist/config/user-config.js.map +1 -1
- package/dist/config/validate-policy-dir.d.ts +10 -0
- package/dist/config/validate-policy-dir.js +59 -0
- package/dist/config/validate-policy-dir.js.map +1 -0
- package/dist/cron/compile-task-policy.js +1 -0
- package/dist/cron/compile-task-policy.js.map +1 -1
- package/dist/cron/job-commands.js +22 -2
- package/dist/cron/job-commands.js.map +1 -1
- package/dist/cron/types.d.ts +16 -2
- package/dist/cron/types.js +1 -3
- package/dist/cron/types.js.map +1 -1
- package/dist/daemon/daemon-command.js +16 -2
- package/dist/daemon/daemon-command.js.map +1 -1
- package/dist/daemon/ironcurtain-daemon.d.ts +44 -1
- package/dist/daemon/ironcurtain-daemon.js +210 -8
- package/dist/daemon/ironcurtain-daemon.js.map +1 -1
- package/dist/docker/adapters/claude-code.d.ts +2 -1
- package/dist/docker/adapters/claude-code.js +320 -131
- package/dist/docker/adapters/claude-code.js.map +1 -1
- package/dist/docker/adapters/goose.d.ts +12 -11
- package/dist/docker/adapters/goose.js +80 -27
- package/dist/docker/adapters/goose.js.map +1 -1
- package/dist/docker/agent-adapter.d.ts +178 -4
- package/dist/docker/agent-adapter.js +14 -0
- package/dist/docker/agent-adapter.js.map +1 -1
- package/dist/docker/agent-registry.js +3 -2
- package/dist/docker/agent-registry.js.map +1 -1
- package/dist/docker/code-mode-proxy.d.ts +27 -0
- package/dist/docker/code-mode-proxy.js +3 -0
- package/dist/docker/code-mode-proxy.js.map +1 -1
- package/dist/docker/container-lifecycle.d.ts +15 -0
- package/dist/docker/container-lifecycle.js +32 -0
- package/dist/docker/container-lifecycle.js.map +1 -0
- package/dist/docker/docker-agent-session.d.ts +113 -62
- package/dist/docker/docker-agent-session.js +241 -300
- package/dist/docker/docker-agent-session.js.map +1 -1
- package/dist/docker/docker-infrastructure.d.ts +256 -21
- package/dist/docker/docker-infrastructure.js +585 -30
- package/dist/docker/docker-infrastructure.js.map +1 -1
- package/dist/docker/docker-manager.d.ts +36 -1
- package/dist/docker/docker-manager.js +168 -26
- package/dist/docker/docker-manager.js.map +1 -1
- package/dist/docker/docker-progress-sink.d.ts +43 -0
- package/dist/docker/docker-progress-sink.js +299 -0
- package/dist/docker/docker-progress-sink.js.map +1 -0
- package/dist/docker/mitm-proxy.d.ts +90 -1
- package/dist/docker/mitm-proxy.js +720 -40
- package/dist/docker/mitm-proxy.js.map +1 -1
- package/dist/docker/oauth-credentials.d.ts +46 -3
- package/dist/docker/oauth-credentials.js +56 -14
- package/dist/docker/oauth-credentials.js.map +1 -1
- package/dist/docker/oauth-token-manager.js +2 -2
- package/dist/docker/oauth-token-manager.js.map +1 -1
- package/dist/docker/orientation.d.ts +1 -1
- package/dist/docker/provider-config.d.ts +81 -2
- package/dist/docker/provider-config.js +222 -28
- package/dist/docker/provider-config.js.map +1 -1
- package/dist/docker/proxy-tools.d.ts +0 -2
- package/dist/docker/proxy-tools.js +2 -7
- package/dist/docker/proxy-tools.js.map +1 -1
- package/dist/docker/pty-session.d.ts +49 -1
- package/dist/docker/pty-session.js +212 -90
- package/dist/docker/pty-session.js.map +1 -1
- package/dist/docker/pty-types.d.ts +0 -2
- package/dist/docker/pty-types.js +0 -2
- package/dist/docker/pty-types.js.map +1 -1
- package/dist/docker/resource-limits.d.ts +125 -0
- package/dist/docker/resource-limits.js +200 -0
- package/dist/docker/resource-limits.js.map +1 -0
- package/dist/docker/spawn-with-idle-timeout.d.ts +39 -0
- package/dist/docker/spawn-with-idle-timeout.js +152 -0
- package/dist/docker/spawn-with-idle-timeout.js.map +1 -0
- package/dist/docker/sse-extractor.d.ts +51 -0
- package/dist/docker/sse-extractor.js +303 -0
- package/dist/docker/sse-extractor.js.map +1 -0
- package/dist/docker/token-stream-bus.d.ts +83 -0
- package/dist/docker/token-stream-bus.js +98 -0
- package/dist/docker/token-stream-bus.js.map +1 -0
- package/dist/docker/token-stream-types.d.ts +71 -0
- package/dist/docker/token-stream-types.js +8 -0
- package/dist/docker/token-stream-types.js.map +1 -0
- package/dist/docker/types.d.ts +61 -6
- package/dist/doctor/checks.d.ts +138 -0
- package/dist/doctor/checks.js +511 -0
- package/dist/doctor/checks.js.map +1 -0
- package/dist/doctor/doctor-command.d.ts +29 -0
- package/dist/doctor/doctor-command.js +160 -0
- package/dist/doctor/doctor-command.js.map +1 -0
- package/dist/doctor/mcp-liveness.d.ts +28 -0
- package/dist/doctor/mcp-liveness.js +84 -0
- package/dist/doctor/mcp-liveness.js.map +1 -0
- package/dist/doctor/oauth-checks.d.ts +23 -0
- package/dist/doctor/oauth-checks.js +157 -0
- package/dist/doctor/oauth-checks.js.map +1 -0
- package/dist/doctor/output.d.ts +26 -0
- package/dist/doctor/output.js +82 -0
- package/dist/doctor/output.js.map +1 -0
- package/dist/escalation/listener-command.js +0 -3
- package/dist/escalation/listener-command.js.map +1 -1
- package/dist/event-bus/typed-event-bus.d.ts +26 -0
- package/dist/event-bus/typed-event-bus.js +35 -0
- package/dist/event-bus/typed-event-bus.js.map +1 -0
- package/dist/index.js +32 -9
- package/dist/index.js.map +1 -1
- package/dist/logger.js +31 -2
- package/dist/logger.js.map +1 -1
- package/dist/memory/auto-save.d.ts +18 -4
- package/dist/memory/auto-save.js +46 -6
- package/dist/memory/auto-save.js.map +1 -1
- package/dist/memory/memory-policy.d.ts +41 -0
- package/dist/memory/memory-policy.js +36 -0
- package/dist/memory/memory-policy.js.map +1 -0
- package/dist/mux/mux-app.d.ts +2 -0
- package/dist/mux/mux-app.js +30 -34
- package/dist/mux/mux-app.js.map +1 -1
- package/dist/mux/mux-command.d.ts +10 -1
- package/dist/mux/mux-command.js +62 -35
- package/dist/mux/mux-command.js.map +1 -1
- package/dist/mux/mux-renderer.js +22 -17
- package/dist/mux/mux-renderer.js.map +1 -1
- package/dist/mux/pty-bridge.d.ts +2 -0
- package/dist/mux/pty-bridge.js +3 -0
- package/dist/mux/pty-bridge.js.map +1 -1
- package/dist/mux/session-scanner.d.ts +7 -0
- package/dist/mux/session-scanner.js +21 -2
- package/dist/mux/session-scanner.js.map +1 -1
- package/dist/mux/types.d.ts +0 -4
- package/dist/mux/types.js.map +1 -1
- package/dist/{pipeline → observability}/llm-logger.d.ts +9 -3
- package/dist/{pipeline → observability}/llm-logger.js +16 -4
- package/dist/observability/llm-logger.js.map +1 -0
- package/dist/observe/observe-command.d.ts +12 -0
- package/dist/observe/observe-command.js +298 -0
- package/dist/observe/observe-command.js.map +1 -0
- package/dist/observe/observe-renderer.d.ts +24 -0
- package/dist/observe/observe-renderer.js +87 -0
- package/dist/observe/observe-renderer.js.map +1 -0
- package/dist/observe/observe-tui-rain.d.ts +42 -0
- package/dist/observe/observe-tui-rain.js +531 -0
- package/dist/observe/observe-tui-rain.js.map +1 -0
- package/dist/observe/observe-tui-text-panel.d.ts +46 -0
- package/dist/observe/observe-tui-text-panel.js +525 -0
- package/dist/observe/observe-tui-text-panel.js.map +1 -0
- package/dist/observe/observe-tui-types.d.ts +270 -0
- package/dist/observe/observe-tui-types.js +200 -0
- package/dist/observe/observe-tui-types.js.map +1 -0
- package/dist/observe/observe-tui-word-scorer.d.ts +143 -0
- package/dist/observe/observe-tui-word-scorer.js +572 -0
- package/dist/observe/observe-tui-word-scorer.js.map +1 -0
- package/dist/observe/observe-tui.d.ts +59 -0
- package/dist/observe/observe-tui.js +592 -0
- package/dist/observe/observe-tui.js.map +1 -0
- package/dist/persona/compile-persona-policy.js +1 -0
- package/dist/persona/compile-persona-policy.js.map +1 -1
- package/dist/persona/memory-gate.d.ts +19 -0
- package/dist/persona/memory-gate.js +33 -0
- package/dist/persona/memory-gate.js.map +1 -0
- package/dist/persona/persona-command.js +41 -6
- package/dist/persona/persona-command.js.map +1 -1
- package/dist/persona/resolve.d.ts +22 -0
- package/dist/persona/resolve.js +45 -2
- package/dist/persona/resolve.js.map +1 -1
- package/dist/persona/types.d.ts +16 -0
- package/dist/pipeline/annotate.d.ts +7 -1
- package/dist/pipeline/annotate.js +92 -2
- package/dist/pipeline/annotate.js.map +1 -1
- package/dist/pipeline/compile.js +2 -0
- package/dist/pipeline/compile.js.map +1 -1
- package/dist/pipeline/constitution-compiler.d.ts +25 -14
- package/dist/pipeline/constitution-compiler.js +107 -38
- package/dist/pipeline/constitution-compiler.js.map +1 -1
- package/dist/pipeline/dynamic-list-types.d.ts +3 -6
- package/dist/pipeline/dynamic-list-types.js +3 -16
- package/dist/pipeline/dynamic-list-types.js.map +1 -1
- package/dist/pipeline/parallel-progress.d.ts +59 -0
- package/dist/pipeline/parallel-progress.js +263 -0
- package/dist/pipeline/parallel-progress.js.map +1 -0
- package/dist/pipeline/pipeline-runner.d.ts +49 -8
- package/dist/pipeline/pipeline-runner.js +419 -231
- package/dist/pipeline/pipeline-runner.js.map +1 -1
- package/dist/pipeline/pipeline-shared.d.ts +59 -1
- package/dist/pipeline/pipeline-shared.js +127 -12
- package/dist/pipeline/pipeline-shared.js.map +1 -1
- package/dist/pipeline/policy-verifier.js +16 -3
- package/dist/pipeline/policy-verifier.js.map +1 -1
- package/dist/pipeline/proxy-mcp-connections.js +17 -2
- package/dist/pipeline/proxy-mcp-connections.js.map +1 -1
- package/dist/pipeline/scenario-generator.d.ts +4 -8
- package/dist/pipeline/scenario-generator.js +13 -46
- package/dist/pipeline/scenario-generator.js.map +1 -1
- package/dist/pipeline/server-prefilter.d.ts +31 -0
- package/dist/pipeline/server-prefilter.js +115 -0
- package/dist/pipeline/server-prefilter.js.map +1 -0
- package/dist/pipeline/types.d.ts +5 -0
- package/dist/sandbox/index.d.ts +14 -0
- package/dist/sandbox/index.js +335 -116
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/ironcurtain-protocol.d.ts +52 -0
- package/dist/sandbox/ironcurtain-protocol.js +185 -0
- package/dist/sandbox/ironcurtain-protocol.js.map +1 -0
- package/dist/session/agent-session.d.ts +6 -0
- package/dist/session/agent-session.js +16 -5
- package/dist/session/agent-session.js.map +1 -1
- package/dist/session/cli-transport.js +0 -6
- package/dist/session/cli-transport.js.map +1 -1
- package/dist/session/index.d.ts +35 -14
- package/dist/session/index.js +324 -130
- package/dist/session/index.js.map +1 -1
- package/dist/session/preflight.d.ts +50 -13
- package/dist/session/preflight.js +252 -80
- package/dist/session/preflight.js.map +1 -1
- package/dist/session/prompts.d.ts +2 -5
- package/dist/session/prompts.js.map +1 -1
- package/dist/session/session-manager.d.ts +61 -6
- package/dist/session/session-manager.js +72 -10
- package/dist/session/session-manager.js.map +1 -1
- package/dist/session/session-metadata.d.ts +21 -5
- package/dist/session/session-metadata.js +34 -12
- package/dist/session/session-metadata.js.map +1 -1
- package/dist/session/types.d.ts +311 -5
- package/dist/session/types.js +57 -1
- package/dist/session/types.js.map +1 -1
- package/dist/signal/setup-signal.js +13 -21
- package/dist/signal/setup-signal.js.map +1 -1
- package/dist/signal/signal-bot-daemon.d.ts +16 -1
- package/dist/signal/signal-bot-daemon.js +39 -27
- package/dist/signal/signal-bot-daemon.js.map +1 -1
- package/dist/signal/signal-container.js +7 -1
- package/dist/signal/signal-container.js.map +1 -1
- package/dist/skills/discovery.d.ts +77 -0
- package/dist/skills/discovery.js +206 -0
- package/dist/skills/discovery.js.map +1 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +3 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/staging.d.ts +28 -0
- package/dist/skills/staging.js +68 -0
- package/dist/skills/staging.js.map +1 -0
- package/dist/skills/types.d.ts +46 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/trusted-process/approval-whitelist.d.ts +0 -2
- package/dist/trusted-process/approval-whitelist.js +17 -17
- package/dist/trusted-process/approval-whitelist.js.map +1 -1
- package/dist/trusted-process/async-mutex.d.ts +32 -0
- package/dist/trusted-process/async-mutex.js +50 -0
- package/dist/trusted-process/async-mutex.js.map +1 -0
- package/dist/trusted-process/audit-log.d.ts +38 -1
- package/dist/trusted-process/audit-log.js +82 -13
- package/dist/trusted-process/audit-log.js.map +1 -1
- package/dist/trusted-process/call-circuit-breaker.js +2 -1
- package/dist/trusted-process/call-circuit-breaker.js.map +1 -1
- package/dist/trusted-process/control-server.d.ts +101 -0
- package/dist/trusted-process/control-server.js +282 -0
- package/dist/trusted-process/control-server.js.map +1 -0
- package/dist/trusted-process/domain-utils.d.ts +6 -6
- package/dist/trusted-process/domain-utils.js +7 -23
- package/dist/trusted-process/domain-utils.js.map +1 -1
- package/dist/trusted-process/error-prefixes.d.ts +16 -0
- package/dist/trusted-process/error-prefixes.js +17 -0
- package/dist/trusted-process/error-prefixes.js.map +1 -0
- package/dist/trusted-process/index.d.ts +40 -23
- package/dist/trusted-process/index.js +131 -225
- package/dist/trusted-process/index.js.map +1 -1
- package/dist/trusted-process/list-matcher.d.ts +17 -0
- package/dist/trusted-process/list-matcher.js +31 -0
- package/dist/trusted-process/list-matcher.js.map +1 -0
- package/dist/trusted-process/mcp-client-manager.d.ts +35 -6
- package/dist/trusted-process/mcp-client-manager.js +40 -45
- package/dist/trusted-process/mcp-client-manager.js.map +1 -1
- package/dist/trusted-process/mcp-proxy-server.d.ts +31 -120
- package/dist/trusted-process/mcp-proxy-server.js +166 -657
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
- package/dist/trusted-process/policy-engine.d.ts +16 -4
- package/dist/trusted-process/policy-engine.js +90 -45
- package/dist/trusted-process/policy-engine.js.map +1 -1
- package/dist/trusted-process/policy-roots.d.ts +6 -0
- package/dist/trusted-process/policy-roots.js +20 -0
- package/dist/trusted-process/policy-roots.js.map +1 -1
- package/dist/trusted-process/sandbox-integration.d.ts +12 -0
- package/dist/trusted-process/sandbox-integration.js +26 -0
- package/dist/trusted-process/sandbox-integration.js.map +1 -1
- package/dist/trusted-process/tool-call-coordinator.d.ts +269 -0
- package/dist/trusted-process/tool-call-coordinator.js +508 -0
- package/dist/trusted-process/tool-call-coordinator.js.map +1 -0
- package/dist/trusted-process/tool-call-pipeline.d.ts +205 -0
- package/dist/trusted-process/tool-call-pipeline.js +798 -0
- package/dist/trusted-process/tool-call-pipeline.js.map +1 -0
- package/dist/trusted-process/tool-description-hints.d.ts +1 -1
- package/dist/types/argument-roles.d.ts +1 -1
- package/dist/types/argument-roles.js +14 -1
- package/dist/types/argument-roles.js.map +1 -1
- package/dist/types/audit.d.ts +9 -0
- package/dist/{session → types}/errors.d.ts +8 -2
- package/dist/{session → types}/errors.js +8 -2
- package/dist/types/errors.js.map +1 -0
- package/dist/types/reserved-servers.d.ts +11 -0
- package/dist/types/reserved-servers.js +12 -0
- package/dist/types/reserved-servers.js.map +1 -0
- package/dist/types/server-listing.d.ts +14 -0
- package/dist/types/server-listing.js +2 -0
- package/dist/types/server-listing.js.map +1 -0
- package/dist/types/slug.d.ts +2 -0
- package/dist/types/slug.js +2 -0
- package/dist/types/slug.js.map +1 -1
- package/dist/types/url-normalize.d.ts +16 -0
- package/dist/types/url-normalize.js +33 -0
- package/dist/types/url-normalize.js.map +1 -0
- package/dist/utils/error-message.d.ts +8 -0
- package/dist/utils/error-message.js +11 -0
- package/dist/utils/error-message.js.map +1 -0
- package/dist/utils/exec-error.d.ts +19 -0
- package/dist/utils/exec-error.js +16 -0
- package/dist/utils/exec-error.js.map +1 -0
- package/dist/utils/is-plain-object.d.ts +7 -0
- package/dist/utils/is-plain-object.js +12 -0
- package/dist/utils/is-plain-object.js.map +1 -0
- package/dist/utils/preflight-checks.d.ts +16 -0
- package/dist/utils/preflight-checks.js +178 -0
- package/dist/utils/preflight-checks.js.map +1 -0
- package/dist/web-ui/__tests__/json-rpc-dispatch.test.d.ts +7 -0
- package/dist/web-ui/__tests__/json-rpc-dispatch.test.js +725 -0
- package/dist/web-ui/__tests__/json-rpc-dispatch.test.js.map +1 -0
- package/dist/web-ui/dispatch/escalation-dispatch.d.ts +7 -0
- package/dist/web-ui/dispatch/escalation-dispatch.js +52 -0
- package/dist/web-ui/dispatch/escalation-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/job-dispatch.d.ts +8 -0
- package/dist/web-ui/dispatch/job-dispatch.js +83 -0
- package/dist/web-ui/dispatch/job-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/persona-dispatch.d.ts +6 -0
- package/dist/web-ui/dispatch/persona-dispatch.js +116 -0
- package/dist/web-ui/dispatch/persona-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/session-dispatch.d.ts +9 -0
- package/dist/web-ui/dispatch/session-dispatch.js +201 -0
- package/dist/web-ui/dispatch/session-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/token-stream-dispatch.d.ts +15 -0
- package/dist/web-ui/dispatch/token-stream-dispatch.js +55 -0
- package/dist/web-ui/dispatch/token-stream-dispatch.js.map +1 -0
- package/dist/web-ui/dispatch/types.d.ts +31 -0
- package/dist/web-ui/dispatch/types.js +68 -0
- package/dist/web-ui/dispatch/types.js.map +1 -0
- package/dist/web-ui/dispatch/workflow-dispatch.d.ts +103 -0
- package/dist/web-ui/dispatch/workflow-dispatch.js +925 -0
- package/dist/web-ui/dispatch/workflow-dispatch.js.map +1 -0
- package/dist/web-ui/json-rpc-dispatch.d.ts +12 -0
- package/dist/web-ui/json-rpc-dispatch.js +34 -0
- package/dist/web-ui/json-rpc-dispatch.js.map +1 -0
- package/dist/web-ui/state-graph.d.ts +13 -0
- package/dist/web-ui/state-graph.js +82 -0
- package/dist/web-ui/state-graph.js.map +1 -0
- package/dist/web-ui/token-stream-bridge.d.ts +76 -0
- package/dist/web-ui/token-stream-bridge.js +265 -0
- package/dist/web-ui/token-stream-bridge.js.map +1 -0
- package/dist/web-ui/web-event-bus.d.ts +152 -0
- package/dist/web-ui/web-event-bus.js +19 -0
- package/dist/web-ui/web-event-bus.js.map +1 -0
- package/dist/web-ui/web-session-transport.d.ts +31 -0
- package/dist/web-ui/web-session-transport.js +109 -0
- package/dist/web-ui/web-session-transport.js.map +1 -0
- package/dist/web-ui/web-ui-server.d.ts +76 -0
- package/dist/web-ui/web-ui-server.js +461 -0
- package/dist/web-ui/web-ui-server.js.map +1 -0
- package/dist/web-ui/web-ui-types.d.ts +290 -0
- package/dist/web-ui/web-ui-types.js +32 -0
- package/dist/web-ui/web-ui-types.js.map +1 -0
- package/dist/web-ui/ws-utils.d.ts +5 -0
- package/dist/web-ui/ws-utils.js +12 -0
- package/dist/web-ui/ws-utils.js.map +1 -0
- package/dist/web-ui-static/assets/index-BFO5z1js.js +76 -0
- package/dist/web-ui-static/assets/index-CCUwrkIL.css +1 -0
- package/dist/web-ui-static/assets/orbitron-latin-700-normal-4jsRXGGJ.woff2 +0 -0
- package/dist/web-ui-static/assets/orbitron-latin-700-normal-DmYOh-W9.woff +0 -0
- package/dist/web-ui-static/index.html +14 -0
- package/dist/workflow/artifacts.d.ts +59 -0
- package/dist/workflow/artifacts.js +188 -0
- package/dist/workflow/artifacts.js.map +1 -0
- package/dist/workflow/checkpoint-selection.d.ts +34 -0
- package/dist/workflow/checkpoint-selection.js +48 -0
- package/dist/workflow/checkpoint-selection.js.map +1 -0
- package/dist/workflow/checkpoint.d.ts +35 -0
- package/dist/workflow/checkpoint.js +84 -0
- package/dist/workflow/checkpoint.js.map +1 -0
- package/dist/workflow/cli-support.d.ts +92 -0
- package/dist/workflow/cli-support.js +423 -0
- package/dist/workflow/cli-support.js.map +1 -0
- package/dist/workflow/definition-loader.d.ts +32 -0
- package/dist/workflow/definition-loader.js +56 -0
- package/dist/workflow/definition-loader.js.map +1 -0
- package/dist/workflow/discovery.d.ts +78 -0
- package/dist/workflow/discovery.js +203 -0
- package/dist/workflow/discovery.js.map +1 -0
- package/dist/workflow/errors.d.ts +66 -0
- package/dist/workflow/errors.js +49 -0
- package/dist/workflow/errors.js.map +1 -0
- package/dist/workflow/guards.d.ts +8 -0
- package/dist/workflow/guards.js +38 -0
- package/dist/workflow/guards.js.map +1 -0
- package/dist/workflow/lint-integration.d.ts +64 -0
- package/dist/workflow/lint-integration.js +56 -0
- package/dist/workflow/lint-integration.js.map +1 -0
- package/dist/workflow/lint.d.ts +66 -0
- package/dist/workflow/lint.js +410 -0
- package/dist/workflow/lint.js.map +1 -0
- package/dist/workflow/machine-builder.d.ts +90 -0
- package/dist/workflow/machine-builder.js +485 -0
- package/dist/workflow/machine-builder.js.map +1 -0
- package/dist/workflow/message-log.d.ts +85 -0
- package/dist/workflow/message-log.js +40 -0
- package/dist/workflow/message-log.js.map +1 -0
- package/dist/workflow/orchestrator.d.ts +450 -0
- package/dist/workflow/orchestrator.js +1941 -0
- package/dist/workflow/orchestrator.js.map +1 -0
- package/dist/workflow/prompt-builder.d.ts +39 -0
- package/dist/workflow/prompt-builder.js +242 -0
- package/dist/workflow/prompt-builder.js.map +1 -0
- package/dist/workflow/run-state-command.d.ts +9 -0
- package/dist/workflow/run-state-command.js +414 -0
- package/dist/workflow/run-state-command.js.map +1 -0
- package/dist/workflow/run-state-debug-capture.d.ts +40 -0
- package/dist/workflow/run-state-debug-capture.js +101 -0
- package/dist/workflow/run-state-debug-capture.js.map +1 -0
- package/dist/workflow/status-parser.d.ts +68 -0
- package/dist/workflow/status-parser.js +259 -0
- package/dist/workflow/status-parser.js.map +1 -0
- package/dist/workflow/types.d.ts +498 -0
- package/dist/workflow/types.js +83 -0
- package/dist/workflow/types.js.map +1 -0
- package/dist/workflow/validate.d.ts +58 -0
- package/dist/workflow/validate.js +499 -0
- package/dist/workflow/validate.js.map +1 -0
- package/dist/workflow/workflow-command.d.ts +26 -0
- package/dist/workflow/workflow-command.js +648 -0
- package/dist/workflow/workflow-command.js.map +1 -0
- package/dist/workflow/workflow-discovery.d.ts +25 -0
- package/dist/workflow/workflow-discovery.js +100 -0
- package/dist/workflow/workflow-discovery.js.map +1 -0
- package/dist/workflow/workflow-manager.d.ts +122 -0
- package/dist/workflow/workflow-manager.js +308 -0
- package/dist/workflow/workflow-manager.js.map +1 -0
- package/dist/workflow/workflows/design-and-code/workflow.yaml +169 -0
- package/dist/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
- package/dist/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
- package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
- package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
- package/dist/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
- package/dist/workflow/workflows/test-email-summary/workflow.yaml +89 -0
- package/dist/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +201 -0
- package/dist/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +166 -0
- package/dist/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +185 -0
- package/dist/workflow/workflows/vuln-discovery/workflow.yaml +920 -0
- package/dist/workflow/worktree.d.ts +43 -0
- package/dist/workflow/worktree.js +106 -0
- package/dist/workflow/worktree.js.map +1 -0
- package/docker/Dockerfile.base +12 -1
- package/docker/Dockerfile.base.arm64 +17 -1
- package/docker/entrypoint-claude-code.sh +46 -0
- package/docker/entrypoint-goose.sh +25 -0
- package/package.json +22 -8
- package/packages/web-ui/README.md +113 -0
- package/packages/web-ui/docs/workflow-state-machine.png +0 -0
- package/src/config/generated/compiled-policy.json +528 -161
- package/src/config/generated/test-scenarios.json +4810 -170
- package/src/config/generated/tool-annotations.json +34 -47
- package/src/config/mcp-servers.json +2 -2
- package/src/workflow/workflows/design-and-code/workflow.yaml +169 -0
- package/src/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
- package/src/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
- package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
- package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
- package/src/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
- package/src/workflow/workflows/test-email-summary/workflow.yaml +89 -0
- package/src/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +201 -0
- package/src/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +166 -0
- package/src/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +185 -0
- package/src/workflow/workflows/vuln-discovery/workflow.yaml +920 -0
- package/dist/pipeline/llm-logger.js.map +0 -1
- package/dist/session/errors.js.map +0 -1
|
@@ -12,26 +12,33 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { resolve } from 'node:path';
|
|
14
14
|
import chalk from 'chalk';
|
|
15
|
-
import
|
|
15
|
+
import pLimit from 'p-limit';
|
|
16
|
+
import { extractServerDomainAllowlists, findAnnotationServerDrift, safeForDisplay } from '../config/index.js';
|
|
17
|
+
import { createLanguageModel } from '../config/model-provider.js';
|
|
18
|
+
import { loadUserConfig } from '../config/user-config.js';
|
|
16
19
|
import { PolicyEngine } from '../trusted-process/policy-engine.js';
|
|
17
20
|
import { buildCompilerSystemPrompt, ConstitutionCompilerSession, formatAnnotationsSummary, validateCompiledRules, } from './constitution-compiler.js';
|
|
18
21
|
import { getHandwrittenScenarios } from './handwritten-scenarios.js';
|
|
19
22
|
import { resolveAllLists } from './list-resolver.js';
|
|
20
|
-
import { computeHash, loadExistingArtifact, loadStoredToolAnnotationsFile, resolveRulePaths, writeArtifact,
|
|
23
|
+
import { computeHash, loadExistingArtifact, loadStoredToolAnnotationsFile, resolveRulePaths, writeArtifact, createPipelineLlm, createPerServerModel, createThrottledModel, SpinnerProgressReporter, } from './pipeline-shared.js';
|
|
21
24
|
import { resolveStoredAnnotationsFile } from '../types/argument-roles.js';
|
|
22
25
|
import { applyScenarioCorrections, buildJudgeSystemPrompt, detectAllDefaultRoleFallbacks, extractScenarioCorrections, filterStructuralConflicts, PolicyVerifierSession, verifyPolicy, } from './policy-verifier.js';
|
|
23
26
|
import { filterInvalidSchemaScenarios } from './scenario-schema-validator.js';
|
|
24
27
|
import { buildGeneratorSystemPrompt, generateScenarios, repairScenarios } from './scenario-generator.js';
|
|
28
|
+
import { prefilterServers } from './server-prefilter.js';
|
|
25
29
|
import { connectViaProxy } from './proxy-mcp-connections.js';
|
|
26
30
|
/** Creates PipelineModels from user config. Delegates to shared createPipelineLlm. */
|
|
27
31
|
export async function createPipelineModels(logDir) {
|
|
28
32
|
const effectiveLogDir = logDir ?? resolve(process.cwd(), 'generated');
|
|
29
33
|
const llm = await createPipelineLlm(effectiveLogDir, 'unknown');
|
|
34
|
+
const userConfig = loadUserConfig();
|
|
35
|
+
const haikuBaseLlm = await createLanguageModel(userConfig.prefilterModelId, userConfig);
|
|
36
|
+
const { model: prefilterModel } = createPerServerModel(haikuBaseLlm, llm.logPath, 'prefilter');
|
|
30
37
|
return {
|
|
31
|
-
|
|
38
|
+
baseLlm: llm.baseLlm,
|
|
32
39
|
cacheStrategy: llm.cacheStrategy,
|
|
33
|
-
logContext: llm.logContext,
|
|
34
40
|
logPath: llm.logPath,
|
|
41
|
+
prefilterModel,
|
|
35
42
|
};
|
|
36
43
|
}
|
|
37
44
|
// ---------------------------------------------------------------------------
|
|
@@ -40,6 +47,29 @@ export async function createPipelineModels(logDir) {
|
|
|
40
47
|
function computeScenariosHash(systemPrompt, handwrittenScenarios) {
|
|
41
48
|
return computeHash(systemPrompt, JSON.stringify(handwrittenScenarios));
|
|
42
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Preventive warning: if `mcp-servers.json` declares servers that are not
|
|
52
|
+
* present in `tool-annotations.json`, compiling will produce a policy with
|
|
53
|
+
* no rules for those servers and every call to them will default-deny at
|
|
54
|
+
* runtime. Surface this before starting the (slow) compile so the user
|
|
55
|
+
* can re-run the per-server annotator first.
|
|
56
|
+
*
|
|
57
|
+
* `mcpServers` may be undefined when compile-policy is run with `--no-mcp`,
|
|
58
|
+
* in which case the check is skipped.
|
|
59
|
+
*/
|
|
60
|
+
function warnOnCompileAnnotationGaps(mcpServers, storedAnnotationsFile) {
|
|
61
|
+
if (!mcpServers)
|
|
62
|
+
return;
|
|
63
|
+
const { missing } = findAnnotationServerDrift(storedAnnotationsFile, mcpServers);
|
|
64
|
+
if (missing.length === 0)
|
|
65
|
+
return;
|
|
66
|
+
console.error(chalk.yellow(`Warning: ${missing.length} configured MCP server(s) have no tool annotations: ${missing.map(safeForDisplay).join(', ')}.`));
|
|
67
|
+
console.error(chalk.yellow(' The compiled policy will have no rules for these servers and their tools will be denied.'));
|
|
68
|
+
for (const name of missing) {
|
|
69
|
+
console.error(chalk.yellow(` Run \`ironcurtain annotate-tools --server ${safeForDisplay(name)}\` to annotate this server.`));
|
|
70
|
+
}
|
|
71
|
+
console.error('');
|
|
72
|
+
}
|
|
43
73
|
function extractPermittedDirectories(rules) {
|
|
44
74
|
const dirs = new Set();
|
|
45
75
|
for (const rule of rules) {
|
|
@@ -52,7 +82,8 @@ function extractPermittedDirectories(rules) {
|
|
|
52
82
|
function collectProbeScenarios(result) {
|
|
53
83
|
return result.rounds.flatMap((round) => round.newScenarios);
|
|
54
84
|
}
|
|
55
|
-
function filterAndLogStructuralConflicts(engine, scenarios, label = 'Discarded scenario (structural conflict)', storedAnnotations) {
|
|
85
|
+
function filterAndLogStructuralConflicts(engine, scenarios, label = 'Discarded scenario (structural conflict)', storedAnnotations, reporter) {
|
|
86
|
+
const warn = (msg) => (reporter ? reporter.warn(msg) : console.error(msg));
|
|
56
87
|
const filterResult = filterStructuralConflicts(engine, scenarios);
|
|
57
88
|
let valid = filterResult.valid;
|
|
58
89
|
const discarded = filterResult.discarded;
|
|
@@ -60,7 +91,7 @@ function filterAndLogStructuralConflicts(engine, scenarios, label = 'Discarded s
|
|
|
60
91
|
const prefix = d.scenario.source === 'handwritten'
|
|
61
92
|
? chalk.yellow('Warning: handwritten scenario conflicts with structural invariant:')
|
|
62
93
|
: chalk.dim(`${label}:`);
|
|
63
|
-
|
|
94
|
+
warn(` ${prefix} "${d.scenario.description}" — ${d.rule} always returns ${d.actual}`);
|
|
64
95
|
}
|
|
65
96
|
// Discard scenarios whose arguments don't match any conditional role spec —
|
|
66
97
|
// they fall back to all default roles and test the wrong thing.
|
|
@@ -74,7 +105,7 @@ function filterAndLogStructuralConflicts(engine, scenarios, label = 'Discarded s
|
|
|
74
105
|
const w = fallbackWarnings.find((fw) => fw.scenario.description === s.description);
|
|
75
106
|
const details = w?.details.join('; ') ?? 'unknown conditional mismatch';
|
|
76
107
|
const discardLabel = 'Discarded (default role fallback)';
|
|
77
|
-
|
|
108
|
+
warn(` ${chalk.dim(`${discardLabel}:`)} "${s.description}" — ${details}`);
|
|
78
109
|
discarded.push({ scenario: s, rule: 'default-role-fallback', actual: 'deny' });
|
|
79
110
|
}
|
|
80
111
|
else {
|
|
@@ -88,7 +119,7 @@ function filterAndLogStructuralConflicts(engine, scenarios, label = 'Discarded s
|
|
|
88
119
|
const schemaResult = filterInvalidSchemaScenarios(valid, storedAnnotations);
|
|
89
120
|
if (schemaResult.discarded.length > 0) {
|
|
90
121
|
for (const d of schemaResult.discarded) {
|
|
91
|
-
|
|
122
|
+
warn(` ${chalk.dim('Discarded (schema mismatch):')} "${d.scenario.description}" — ${d.rule}`);
|
|
92
123
|
}
|
|
93
124
|
discarded.push(...schemaResult.discarded);
|
|
94
125
|
valid = schemaResult.valid;
|
|
@@ -96,6 +127,10 @@ function filterAndLogStructuralConflicts(engine, scenarios, label = 'Discarded s
|
|
|
96
127
|
}
|
|
97
128
|
return { valid, discarded };
|
|
98
129
|
}
|
|
130
|
+
/** Coerce an unknown thrown value into an Error. */
|
|
131
|
+
function toError(err) {
|
|
132
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
133
|
+
}
|
|
99
134
|
class RuleValidationError extends Error {
|
|
100
135
|
errors;
|
|
101
136
|
constructor(errors) {
|
|
@@ -104,11 +139,12 @@ class RuleValidationError extends Error {
|
|
|
104
139
|
this.name = 'RuleValidationError';
|
|
105
140
|
}
|
|
106
141
|
}
|
|
107
|
-
function validateRulesOrThrow(rules, listDefinitions = []) {
|
|
142
|
+
function validateRulesOrThrow(rules, listDefinitions = [], reporter) {
|
|
143
|
+
const warn = (msg) => (reporter ? reporter.warn(msg) : console.error(msg));
|
|
108
144
|
const ruleValidation = validateCompiledRules(rules, listDefinitions);
|
|
109
145
|
if (ruleValidation.warnings.length > 0) {
|
|
110
146
|
for (const w of ruleValidation.warnings) {
|
|
111
|
-
|
|
147
|
+
warn(` ${chalk.yellow('Warning:')} ${w}`);
|
|
112
148
|
}
|
|
113
149
|
}
|
|
114
150
|
if (!ruleValidation.valid) {
|
|
@@ -262,7 +298,7 @@ export function deduplicateListDefinitions(defs) {
|
|
|
262
298
|
* Rules are concatenated in alphabetical server order for determinism.
|
|
263
299
|
* List definitions are deduplicated by name.
|
|
264
300
|
*/
|
|
265
|
-
export function mergeServerResults(results, constitutionHash) {
|
|
301
|
+
export function mergeServerResults(results, constitutionHash, skippedServers) {
|
|
266
302
|
const sortedResults = [...results].sort((a, b) => a.serverName.localeCompare(b.serverName));
|
|
267
303
|
const allRules = sortedResults.flatMap((r) => r.rules);
|
|
268
304
|
const allListDefs = sortedResults.flatMap((r) => r.listDefinitions);
|
|
@@ -277,6 +313,12 @@ export function mergeServerResults(results, constitutionHash) {
|
|
|
277
313
|
if (uniqueListDefs.length > 0) {
|
|
278
314
|
artifact.listDefinitions = uniqueListDefs;
|
|
279
315
|
}
|
|
316
|
+
// skippedServers is informational metadata — not read by PolicyEngine.
|
|
317
|
+
// It's intentionally excluded from inputHash; the constitutionHash already
|
|
318
|
+
// changes when the constitution changes, which drives different skip decisions.
|
|
319
|
+
if (skippedServers && skippedServers.length > 0) {
|
|
320
|
+
artifact.skippedServers = skippedServers;
|
|
321
|
+
}
|
|
280
322
|
return artifact;
|
|
281
323
|
}
|
|
282
324
|
// ---------------------------------------------------------------------------
|
|
@@ -291,13 +333,15 @@ export function mergeServerResults(results, constitutionHash) {
|
|
|
291
333
|
* (broad principles) and 'task-policy' (strict whitelist) modes.
|
|
292
334
|
*/
|
|
293
335
|
export class PipelineRunner {
|
|
294
|
-
|
|
336
|
+
baseLlm;
|
|
295
337
|
cacheStrategy;
|
|
296
|
-
|
|
338
|
+
logPath;
|
|
339
|
+
prefilterModel;
|
|
297
340
|
constructor(models) {
|
|
298
|
-
this.
|
|
341
|
+
this.baseLlm = models.baseLlm;
|
|
299
342
|
this.cacheStrategy = models.cacheStrategy;
|
|
300
|
-
this.
|
|
343
|
+
this.prefilterModel = models.prefilterModel;
|
|
344
|
+
this.logPath = models.logPath;
|
|
301
345
|
}
|
|
302
346
|
/**
|
|
303
347
|
* Runs the full pipeline. Returns the compiled policy on success.
|
|
@@ -327,13 +371,14 @@ export class PipelineRunner {
|
|
|
327
371
|
if (!storedAnnotationsFile) {
|
|
328
372
|
throw new Error("tool-annotations.json not found. Run 'ironcurtain annotate-tools' first.");
|
|
329
373
|
}
|
|
374
|
+
warnOnCompileAnnotationGaps(config.mcpServers, storedAnnotationsFile);
|
|
330
375
|
// Resolve conditional role specs to flat annotations for compiler prompts
|
|
331
376
|
const toolAnnotationsFile = resolveStoredAnnotationsFile(storedAnnotationsFile);
|
|
332
377
|
const constitutionHash = computeHash(config.constitutionInput);
|
|
333
|
-
// Phase 1: Per-server compilation
|
|
334
|
-
const serverResults = await this.compileAllServers(config, toolAnnotationsFile, constitutionHash, storedAnnotationsFile);
|
|
378
|
+
// Phase 1: Per-server compilation (with optional Haiku pre-filter)
|
|
379
|
+
const { results: serverResults, skippedServers } = await this.compileAllServers(config, toolAnnotationsFile, constitutionHash, storedAnnotationsFile);
|
|
335
380
|
// Phase 2: Merge
|
|
336
|
-
const mergedPolicy = mergeServerResults(serverResults, constitutionHash);
|
|
381
|
+
const mergedPolicy = mergeServerResults(serverResults, constitutionHash, skippedServers);
|
|
337
382
|
writeArtifact(config.outputDir, 'compiled-policy.json', mergedPolicy);
|
|
338
383
|
// Merge scenarios for the global artifact
|
|
339
384
|
const allScenarios = serverResults.flatMap((r) => r.scenarios);
|
|
@@ -391,11 +436,11 @@ export class PipelineRunner {
|
|
|
391
436
|
return mergedPolicy;
|
|
392
437
|
}
|
|
393
438
|
/**
|
|
394
|
-
* Compiles rules for all servers
|
|
395
|
-
*
|
|
439
|
+
* Compiles rules for all servers, in parallel when multiple servers are present.
|
|
440
|
+
* Single-server compilation uses sequential spinner-based output.
|
|
441
|
+
* Multi-server compilation uses parallel execution with a multi-line progress display.
|
|
396
442
|
*/
|
|
397
443
|
async compileAllServers(config, toolAnnotationsFile, constitutionHash, storedAnnotationsFile) {
|
|
398
|
-
const results = [];
|
|
399
444
|
const serverEntries = Object.entries(toolAnnotationsFile.servers);
|
|
400
445
|
// Apply server filter if provided
|
|
401
446
|
const { serverFilter } = config;
|
|
@@ -406,35 +451,126 @@ export class PipelineRunner {
|
|
|
406
451
|
throw new Error(`No matching servers found for filter: ${config.serverFilter.join(', ')}. ` +
|
|
407
452
|
`Available: ${serverEntries.map(([n]) => n).join(', ')}`);
|
|
408
453
|
}
|
|
454
|
+
// Pre-filter with Haiku: skip servers irrelevant to the input text
|
|
455
|
+
let entriesToCompile = filteredEntries;
|
|
456
|
+
const skippedServers = [];
|
|
457
|
+
if (config.prefilterText !== undefined && !config.serverFilter) {
|
|
458
|
+
const prefilterText = config.prefilterText;
|
|
459
|
+
if (prefilterText.trim() === '') {
|
|
460
|
+
console.error(chalk.yellow('Pre-filter input is empty. All servers skipped (default-deny applies).'));
|
|
461
|
+
const allSkipped = filteredEntries.map(([name]) => ({
|
|
462
|
+
serverName: name,
|
|
463
|
+
reason: 'No input text provided — all servers skipped by default',
|
|
464
|
+
}));
|
|
465
|
+
return { results: [], skippedServers: allSkipped };
|
|
466
|
+
}
|
|
467
|
+
const serverToolPairs = filteredEntries.map(([name]) => [name, toolAnnotationsFile.servers[name].tools]);
|
|
468
|
+
const decisions = await prefilterServers(prefilterText, serverToolPairs, this.prefilterModel, config.constitutionKind);
|
|
469
|
+
const decisionMap = new Map(decisions.map((d) => [d.serverName, d]));
|
|
470
|
+
entriesToCompile = filteredEntries.filter(([name]) => {
|
|
471
|
+
const decision = decisionMap.get(name);
|
|
472
|
+
if (decision?.skip) {
|
|
473
|
+
console.error(` ${chalk.dim(name)}: ${chalk.yellow('skipped')} — ${decision.reason}`);
|
|
474
|
+
skippedServers.push({ serverName: name, reason: decision.reason });
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
return true;
|
|
478
|
+
});
|
|
479
|
+
const skippedCount = filteredEntries.length - entriesToCompile.length;
|
|
480
|
+
if (skippedCount > 0) {
|
|
481
|
+
console.error(` Pre-filter: ${skippedCount} server(s) skipped, ${entriesToCompile.length} proceeding`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (entriesToCompile.length === 0) {
|
|
485
|
+
console.error(chalk.yellow('All servers skipped by pre-filter. Policy will use default-deny only.'));
|
|
486
|
+
return { results: [], skippedServers };
|
|
487
|
+
}
|
|
409
488
|
// Load handwritten scenarios once, then filter per server (avoids N redundant loads)
|
|
410
489
|
const includeHandwritten = config.includeHandwrittenScenarios ?? config.constitutionKind === 'constitution';
|
|
411
490
|
const allHandwrittenScenarios = includeHandwritten ? getHandwrittenScenarios(config.allowedDirectory) : [];
|
|
491
|
+
// Create concurrency controls
|
|
492
|
+
const llmSemaphore = pLimit(8);
|
|
493
|
+
const serverLimit = pLimit(10);
|
|
494
|
+
const buildUnit = (serverName) => ({
|
|
495
|
+
serverName,
|
|
496
|
+
annotations: toolAnnotationsFile.servers[serverName].tools,
|
|
497
|
+
storedAnnotations: storedAnnotationsFile.servers[serverName].tools,
|
|
498
|
+
constitutionText: config.constitutionInput,
|
|
499
|
+
constitutionKind: config.constitutionKind,
|
|
500
|
+
allowedDirectory: config.allowedDirectory,
|
|
501
|
+
protectedPaths: config.protectedPaths,
|
|
502
|
+
mcpServerConfig: config.mcpServers?.[serverName],
|
|
503
|
+
handwrittenScenarios: allHandwrittenScenarios.filter((s) => s.request.serverName === serverName),
|
|
504
|
+
});
|
|
505
|
+
const useParallel = entriesToCompile.length > 1;
|
|
506
|
+
let results;
|
|
507
|
+
if (useParallel) {
|
|
508
|
+
results = await this.compileServersParallel(entriesToCompile, config, constitutionHash, buildUnit, llmSemaphore, serverLimit);
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
results = await this.compileServersSequential(entriesToCompile, config, constitutionHash, buildUnit, llmSemaphore);
|
|
512
|
+
}
|
|
513
|
+
return { results, skippedServers };
|
|
514
|
+
}
|
|
515
|
+
/** Creates a per-server model wrapped with LLM call throttling. */
|
|
516
|
+
createServerModel(serverName, llmSemaphore) {
|
|
517
|
+
const { model, logContext } = createPerServerModel(this.baseLlm, this.logPath, serverName);
|
|
518
|
+
return { model: createThrottledModel(model, llmSemaphore), logContext };
|
|
519
|
+
}
|
|
520
|
+
async compileServersSequential(filteredEntries, config, constitutionHash, buildUnit, llmSemaphore) {
|
|
521
|
+
const results = [];
|
|
412
522
|
const totalServers = filteredEntries.length;
|
|
413
523
|
const failedServers = [];
|
|
414
524
|
for (let i = 0; i < filteredEntries.length; i++) {
|
|
415
|
-
const [serverName
|
|
525
|
+
const [serverName] = filteredEntries[i];
|
|
416
526
|
console.error('');
|
|
417
527
|
console.error(chalk.bold(`[${i + 1}/${totalServers}] Compiling server: ${serverName}`));
|
|
528
|
+
const { model, logContext } = this.createServerModel(serverName, llmSemaphore);
|
|
529
|
+
const reporter = new SpinnerProgressReporter(serverName);
|
|
418
530
|
try {
|
|
419
|
-
const result = await this.compileServer(
|
|
420
|
-
serverName,
|
|
421
|
-
annotations: serverData.tools,
|
|
422
|
-
storedAnnotations: storedAnnotationsFile.servers[serverName].tools,
|
|
423
|
-
constitutionText: config.constitutionInput,
|
|
424
|
-
constitutionKind: config.constitutionKind,
|
|
425
|
-
allowedDirectory: config.allowedDirectory,
|
|
426
|
-
protectedPaths: config.protectedPaths,
|
|
427
|
-
mcpServerConfig: config.mcpServers?.[serverName],
|
|
428
|
-
handwrittenScenarios: allHandwrittenScenarios.filter((s) => s.request.serverName === serverName),
|
|
429
|
-
}, config, constitutionHash);
|
|
531
|
+
const result = await this.compileServer(buildUnit(serverName), config, constitutionHash, model, logContext, reporter);
|
|
430
532
|
results.push(result);
|
|
431
533
|
}
|
|
432
534
|
catch (err) {
|
|
433
|
-
const
|
|
434
|
-
|
|
535
|
+
const coerced = toError(err);
|
|
536
|
+
reporter.fail('compiling', coerced);
|
|
537
|
+
console.error(` ${chalk.red(`Server "${serverName}" failed:`)} ${coerced.message}`);
|
|
538
|
+
failedServers.push(serverName);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return this.handleCompilationResults(results, failedServers);
|
|
542
|
+
}
|
|
543
|
+
async compileServersParallel(filteredEntries, config, constitutionHash, buildUnit, llmSemaphore, serverLimit) {
|
|
544
|
+
// Lazy import to avoid loading parallel-progress.ts when not needed
|
|
545
|
+
const { ParallelProgressDisplay, ParallelProgressReporter } = await import('./parallel-progress.js');
|
|
546
|
+
const serverNames = filteredEntries.map(([name]) => name);
|
|
547
|
+
const display = new ParallelProgressDisplay(serverNames);
|
|
548
|
+
const settled = await Promise.allSettled(filteredEntries.map(([serverName]) => serverLimit(async () => {
|
|
549
|
+
const { model, logContext } = this.createServerModel(serverName, llmSemaphore);
|
|
550
|
+
const reporter = new ParallelProgressReporter(display, serverName);
|
|
551
|
+
try {
|
|
552
|
+
return await this.compileServer(buildUnit(serverName), config, constitutionHash, model, logContext, reporter);
|
|
553
|
+
}
|
|
554
|
+
catch (err) {
|
|
555
|
+
reporter.fail('compiling', toError(err));
|
|
556
|
+
throw err;
|
|
557
|
+
}
|
|
558
|
+
})));
|
|
559
|
+
display.finish();
|
|
560
|
+
const results = [];
|
|
561
|
+
const failedServers = [];
|
|
562
|
+
for (const [i, outcome] of settled.entries()) {
|
|
563
|
+
if (outcome.status === 'fulfilled') {
|
|
564
|
+
results.push(outcome.value);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
const serverName = filteredEntries[i][0];
|
|
435
568
|
failedServers.push(serverName);
|
|
436
569
|
}
|
|
437
570
|
}
|
|
571
|
+
return this.handleCompilationResults(results, failedServers);
|
|
572
|
+
}
|
|
573
|
+
handleCompilationResults(results, failedServers) {
|
|
438
574
|
if (failedServers.length > 0) {
|
|
439
575
|
console.error(`\n${chalk.yellow(`Warning: ${failedServers.length} server(s) failed verification: ${failedServers.join(', ')}`)}`);
|
|
440
576
|
console.error(chalk.yellow('Continuing with successfully compiled servers.'));
|
|
@@ -448,25 +584,28 @@ export class PipelineRunner {
|
|
|
448
584
|
/**
|
|
449
585
|
* Compiles a single server: compile -> generate scenarios -> verify -> repair.
|
|
450
586
|
* Self-contained compile-verify-repair loop per server.
|
|
587
|
+
*
|
|
588
|
+
* Each server receives its own model (with per-server logging middleware),
|
|
589
|
+
* logContext (for step labeling), and reporter (for progress/warnings).
|
|
590
|
+
* This makes the method safe for concurrent execution.
|
|
451
591
|
*/
|
|
452
|
-
async compileServer(unit, config, constitutionHash) {
|
|
592
|
+
async compileServer(unit, config, constitutionHash, model, logContext, reporter) {
|
|
453
593
|
const serverOutputDir = resolve(config.outputDir, 'servers', unit.serverName);
|
|
454
594
|
// Build per-server system prompt (only this server's annotations)
|
|
455
595
|
const compilerPrompt = unit.constitutionKind === 'task-policy'
|
|
456
|
-
? buildTaskCompilerSystemPrompt(unit.constitutionText, unit.
|
|
457
|
-
: buildCompilerSystemPrompt(unit.constitutionText, unit.
|
|
596
|
+
? buildTaskCompilerSystemPrompt(unit.constitutionText, unit.storedAnnotations, unit.protectedPaths, unit.allowedDirectory, unit.serverName)
|
|
597
|
+
: buildCompilerSystemPrompt(unit.constitutionText, unit.storedAnnotations, { protectedPaths: unit.protectedPaths, allowedDirectory: unit.allowedDirectory }, unit.handwrittenScenarios.length > 0 ? unit.handwrittenScenarios : undefined, { serverScope: unit.serverName });
|
|
458
598
|
// Check per-server cache
|
|
459
599
|
const inputHash = computeServerPolicyHash(unit.serverName, unit.constitutionText, unit.annotations, compilerPrompt);
|
|
460
600
|
const existingServerPolicy = loadExistingArtifact(serverOutputDir, 'compiled-policy.json');
|
|
461
601
|
const existingServerScenarios = loadExistingArtifact(serverOutputDir, 'test-scenarios.json');
|
|
462
602
|
if (existingServerPolicy && existingServerPolicy.inputHash === inputHash && existingServerScenarios) {
|
|
463
|
-
// Verify scenario hash to detect stale scenarios (e.g., changed templates or handwritten scenarios)
|
|
464
603
|
const cachedPermittedDirs = extractPermittedDirectories(resolveRulePaths(existingServerPolicy.rules));
|
|
465
604
|
const cachedDynamicLists = loadExistingArtifact(serverOutputDir, 'dynamic-lists.json');
|
|
466
|
-
const cachedScenarioPrompt = buildGeneratorSystemPrompt(unit.constitutionText, unit.
|
|
605
|
+
const cachedScenarioPrompt = buildGeneratorSystemPrompt(unit.constitutionText, unit.storedAnnotations, unit.allowedDirectory, cachedPermittedDirs, cachedDynamicLists);
|
|
467
606
|
const cachedScenarioHash = computeScenariosHash(cachedScenarioPrompt, unit.handwrittenScenarios);
|
|
468
607
|
if (existingServerScenarios.inputHash === cachedScenarioHash) {
|
|
469
|
-
|
|
608
|
+
reporter.complete('cached', ` ${unit.serverName}: compilation ${chalk.dim('(cached)')}`, 0);
|
|
470
609
|
return {
|
|
471
610
|
serverName: unit.serverName,
|
|
472
611
|
rules: resolveRulePaths(existingServerPolicy.rules),
|
|
@@ -480,20 +619,19 @@ export class PipelineRunner {
|
|
|
480
619
|
}
|
|
481
620
|
const compilerSystem = this.cacheStrategy.wrapSystemPrompt(compilerPrompt);
|
|
482
621
|
// Step 1: Compile rules for this server
|
|
483
|
-
|
|
484
|
-
const compileResult = await this.compileServerPolicyRules(unit, compilerSystem, inputHash);
|
|
622
|
+
logContext.stepName = `compile-${unit.serverName}`;
|
|
623
|
+
const compileResult = await this.compileServerPolicyRules(unit, compilerSystem, inputHash, model, reporter);
|
|
485
624
|
let { rules } = compileResult;
|
|
486
625
|
let { listDefinitions } = compileResult;
|
|
487
|
-
let compilerSession = compileResult.session;
|
|
488
626
|
// Validate server scoping (debug assertion)
|
|
489
627
|
validateServerScoping(unit.serverName, rules);
|
|
490
628
|
// Resolve dynamic lists for this server (before scenario generation so lists are available)
|
|
491
629
|
let dynamicLists;
|
|
492
630
|
if (listDefinitions.length > 0) {
|
|
493
|
-
dynamicLists = await this.resolveServerLists(listDefinitions, serverOutputDir, config, ` ${unit.serverName}
|
|
631
|
+
dynamicLists = await this.resolveServerLists(listDefinitions, serverOutputDir, config, ` ${unit.serverName}`, model, logContext, reporter);
|
|
494
632
|
}
|
|
495
633
|
// Build per-server policy artifact for engine construction
|
|
496
|
-
|
|
634
|
+
const serverPolicyFile = buildPolicyArtifact(constitutionHash, rules, listDefinitions, inputHash);
|
|
497
635
|
// Persist early so intermediate results survive scenario/verification failures
|
|
498
636
|
const writeServerPolicy = () => writeArtifact(serverOutputDir, 'compiled-policy.json', {
|
|
499
637
|
generatedAt: new Date().toISOString(),
|
|
@@ -514,188 +652,239 @@ export class PipelineRunner {
|
|
|
514
652
|
? extractServerDomainAllowlists({ [unit.serverName]: unit.mcpServerConfig })
|
|
515
653
|
: undefined;
|
|
516
654
|
// Step 2: Generate test scenarios for this server
|
|
517
|
-
|
|
655
|
+
logContext.stepName = `scenarios-${unit.serverName}`;
|
|
518
656
|
const permittedDirectories = extractPermittedDirectories(rules);
|
|
519
|
-
const scenarioPrompt = buildGeneratorSystemPrompt(unit.constitutionText, unit.
|
|
657
|
+
const scenarioPrompt = buildGeneratorSystemPrompt(unit.constitutionText, unit.storedAnnotations, unit.allowedDirectory, permittedDirectories, dynamicLists);
|
|
520
658
|
const scenarioHash = computeScenariosHash(scenarioPrompt, unit.handwrittenScenarios);
|
|
521
|
-
const scenarioResult = await this.generateTestScenarios(unit.constitutionText, unit.
|
|
522
|
-
// Step 3: Verify
|
|
523
|
-
this.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
filteredScenarios.push(...validReplacements);
|
|
539
|
-
console.error(` ${chalk.dim(`Repaired ${discardedScenarios.length} discarded scenario(s) → ${validReplacements.length} replacement(s)`)}`);
|
|
540
|
-
}
|
|
659
|
+
const scenarioResult = await this.generateTestScenarios(unit.constitutionText, unit.storedAnnotations, unit.allowedDirectory, unit.handwrittenScenarios, scenarioHash, existingServerScenarios, ` ${unit.serverName}`, permittedDirectories, dynamicLists, model, reporter);
|
|
660
|
+
// Step 3: Verify and repair
|
|
661
|
+
const state = await this.verifyAndRepairServer(unit, config, constitutionHash, inputHash, serverPolicyFile, serverAnnotationsFile, serverDomainAllowlists, dynamicLists, rules, listDefinitions, compileResult.session, scenarioResult, permittedDirectories, compilerSystem, model, logContext, reporter, serverOutputDir, writeServerPolicy);
|
|
662
|
+
// Sync repaired state back so the writeServerPolicy closure sees updated values
|
|
663
|
+
rules = state.rules;
|
|
664
|
+
listDefinitions = state.listDefinitions;
|
|
665
|
+
// Write per-server artifacts (before verification check so they can be inspected on failure)
|
|
666
|
+
const finalScenarios = [...scenarioResult.scenarios, ...state.accumulatedProbes];
|
|
667
|
+
writeServerPolicy();
|
|
668
|
+
writeArtifact(serverOutputDir, 'test-scenarios.json', {
|
|
669
|
+
generatedAt: new Date().toISOString(),
|
|
670
|
+
constitutionHash,
|
|
671
|
+
inputHash: scenarioResult.inputHash,
|
|
672
|
+
scenarios: finalScenarios,
|
|
673
|
+
});
|
|
674
|
+
if (!state.verificationResult.pass) {
|
|
675
|
+
throw new Error(`Verification FAILED for server "${unit.serverName}" — artifacts written for inspection but policy may need review.`);
|
|
541
676
|
}
|
|
677
|
+
const summary = `${state.rules.length} rules, ${finalScenarios.length} scenarios` +
|
|
678
|
+
(state.repairAttempts > 0 ? `, ${state.repairAttempts} repair(s)` : '');
|
|
679
|
+
reporter.done(summary);
|
|
680
|
+
return {
|
|
681
|
+
serverName: unit.serverName,
|
|
682
|
+
rules: state.rules,
|
|
683
|
+
listDefinitions: state.listDefinitions,
|
|
684
|
+
scenarios: finalScenarios,
|
|
685
|
+
inputHash,
|
|
686
|
+
constitutionHash,
|
|
687
|
+
resolvedLists: state.dynamicLists,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Runs initial verification and the compile-verify-repair loop for a server.
|
|
692
|
+
* Returns the mutable compilation state after all repair attempts.
|
|
693
|
+
*/
|
|
694
|
+
async verifyAndRepairServer(unit, config, constitutionHash, inputHash, serverPolicyFile, serverAnnotationsFile, serverDomainAllowlists, dynamicLists, rules, listDefinitions, compilerSession, scenarioResult, permittedDirectories, compilerSystem, model, logContext, reporter, serverOutputDir, writeServerPolicy) {
|
|
695
|
+
logContext.stepName = `verify-${unit.serverName}`;
|
|
696
|
+
const filterEngine = new PolicyEngine(serverPolicyFile, serverAnnotationsFile, unit.protectedPaths, unit.allowedDirectory, undefined, dynamicLists);
|
|
697
|
+
const { filteredScenarios } = await this.filterAndRepairDiscardedScenarios(filterEngine, scenarioResult, unit, permittedDirectories, dynamicLists, model, logContext, reporter);
|
|
542
698
|
const serverToolNames = [...new Set(unit.annotations.map((a) => a.toolName))];
|
|
543
699
|
const serverNames = [unit.serverName];
|
|
544
700
|
const serverTools = unit.annotations.map((a) => ({ serverName: a.serverName, toolName: a.toolName }));
|
|
545
|
-
|
|
546
|
-
|
|
701
|
+
const verifierSystem = this.cacheStrategy.wrapSystemPrompt(buildJudgeSystemPrompt(unit.constitutionText, serverPolicyFile, unit.protectedPaths, serverTools, dynamicLists, unit.allowedDirectory, unit.storedAnnotations));
|
|
702
|
+
const verifierSession = new PolicyVerifierSession({
|
|
547
703
|
system: verifierSystem,
|
|
548
|
-
model
|
|
704
|
+
model,
|
|
549
705
|
serverNames,
|
|
550
706
|
toolNames: serverToolNames,
|
|
551
707
|
storedAnnotations: unit.storedAnnotations,
|
|
552
708
|
});
|
|
709
|
+
reporter.update('verifying');
|
|
710
|
+
const verifyStart = Date.now();
|
|
711
|
+
const verificationResultInitial = await verifyPolicy(unit.constitutionText, serverPolicyFile, serverAnnotationsFile, unit.protectedPaths, filteredScenarios, model, 3, unit.allowedDirectory, (msg) => reporter.update('verifying', msg), serverDomainAllowlists, dynamicLists, verifierSystem, verifierSession, unit.storedAnnotations);
|
|
712
|
+
const verifyElapsed = (Date.now() - verifyStart) / 1000;
|
|
553
713
|
const verifyLabel = ` ${unit.serverName}: Verifying`;
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
let verificationResult = verificationResultInitial;
|
|
560
|
-
if (!verificationResult.pass) {
|
|
561
|
-
this.logVerboseFailures(verificationResult);
|
|
714
|
+
reporter.complete('verifying', verificationResultInitial.pass
|
|
715
|
+
? `${verifyLabel}: ${verificationResultInitial.rounds.length} round(s) (${verifyElapsed.toFixed(1)}s)`
|
|
716
|
+
: `${verifyLabel}: completed with failures (${verifyElapsed.toFixed(1)}s)`, verifyElapsed);
|
|
717
|
+
if (!verificationResultInitial.pass) {
|
|
718
|
+
this.logVerboseFailures(verificationResultInitial, reporter);
|
|
562
719
|
}
|
|
563
720
|
// Collect probe scenarios
|
|
564
|
-
const { valid: filteredInitialProbes } = filterAndLogStructuralConflicts(filterEngine, collectProbeScenarios(
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
721
|
+
const { valid: filteredInitialProbes } = filterAndLogStructuralConflicts(filterEngine, collectProbeScenarios(verificationResultInitial), 'Discarded probe (structural conflict)', unit.storedAnnotations, reporter);
|
|
722
|
+
const state = {
|
|
723
|
+
rules,
|
|
724
|
+
listDefinitions,
|
|
725
|
+
compilerSession,
|
|
726
|
+
dynamicLists,
|
|
727
|
+
serverPolicyFile,
|
|
728
|
+
verificationResult: verificationResultInitial,
|
|
729
|
+
accumulatedProbes: filteredInitialProbes,
|
|
730
|
+
repairAttempts: 0,
|
|
731
|
+
};
|
|
732
|
+
if (!state.verificationResult.pass) {
|
|
733
|
+
await this.runRepairLoop(state, unit, config, constitutionHash, inputHash, serverAnnotationsFile, serverDomainAllowlists, scenarioResult, filterEngine, serverToolNames, serverNames, serverTools, compilerSystem, model, logContext, reporter, serverOutputDir, writeServerPolicy);
|
|
734
|
+
}
|
|
735
|
+
return state;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Filters structurally conflicting scenarios and generates replacements
|
|
739
|
+
* for discarded ones via LLM repair (pre-loop, one-time).
|
|
740
|
+
*/
|
|
741
|
+
async filterAndRepairDiscardedScenarios(filterEngine, scenarioResult, unit, permittedDirectories, dynamicLists, model, logContext, reporter) {
|
|
742
|
+
const { valid: filteredScenarios, discarded: discardedScenarios } = filterAndLogStructuralConflicts(filterEngine, scenarioResult.scenarios, undefined, unit.storedAnnotations, reporter);
|
|
743
|
+
if (discardedScenarios.length > 0) {
|
|
744
|
+
const discardedForRepair = discardedScenarios.map((d) => ({
|
|
745
|
+
scenario: d.scenario,
|
|
746
|
+
feedback: `${d.rule} always returns ${d.actual}`,
|
|
747
|
+
}));
|
|
748
|
+
logContext.stepName = `repair-scenarios-${unit.serverName}`;
|
|
749
|
+
reporter.update('repair-scenarios');
|
|
750
|
+
const repairStart = Date.now();
|
|
751
|
+
const replacementScenarios = await repairScenarios(discardedForRepair, unit.constitutionText, unit.storedAnnotations, unit.allowedDirectory, model, permittedDirectories, dynamicLists, (msg) => reporter.warn(` ${chalk.dim(msg)}`));
|
|
752
|
+
if (replacementScenarios.length > 0) {
|
|
753
|
+
const { valid: validReplacements } = filterAndLogStructuralConflicts(filterEngine, replacementScenarios, 'Discarded replacement (structural conflict)', unit.storedAnnotations, reporter);
|
|
754
|
+
scenarioResult.scenarios.push(...validReplacements);
|
|
755
|
+
filteredScenarios.push(...validReplacements);
|
|
756
|
+
reporter.warn(` ${chalk.dim(`Repaired ${discardedScenarios.length} discarded scenario(s) → ${validReplacements.length} replacement(s)`)}`);
|
|
757
|
+
}
|
|
758
|
+
const repairElapsed = (Date.now() - repairStart) / 1000;
|
|
759
|
+
reporter.complete('repair-scenarios', ` ${unit.serverName}: Repaired scenarios (${repairElapsed.toFixed(1)}s)`, repairElapsed);
|
|
760
|
+
}
|
|
761
|
+
return { filteredScenarios };
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Compile-verify-repair loop (up to 2 repair attempts).
|
|
765
|
+
* Mutates `state` in place with updated rules, lists, and verification results.
|
|
766
|
+
*/
|
|
767
|
+
async runRepairLoop(state, unit, config, constitutionHash, inputHash, serverAnnotationsFile, serverDomainAllowlists, scenarioResult, filterEngine, serverToolNames, serverNames, serverTools, compilerSystem, model, logContext, reporter, serverOutputDir, writeServerPolicy) {
|
|
768
|
+
const MAX_REPAIRS = 3;
|
|
769
|
+
// Each iteration consumes the just-completed verification (initial verify
|
|
770
|
+
// on attempt 0, prior repair-verify on attempts 1..MAX_REPAIRS), exits early
|
|
771
|
+
// if the result is clean, otherwise repairs and re-verifies. Consuming at
|
|
772
|
+
// the top of the loop ensures the final repair-verify's corrections are
|
|
773
|
+
// applied — no post-loop catch-up needed.
|
|
774
|
+
for (let attempt = 0; attempt <= MAX_REPAIRS; attempt++) {
|
|
775
|
+
const lastRound = state.verificationResult.rounds[state.verificationResult.rounds.length - 1];
|
|
776
|
+
const judgeAnalysis = lastRound?.llmAnalysis ?? state.verificationResult.summary;
|
|
777
|
+
const attributedFailures = lastRound?.attributedFailures ?? [];
|
|
778
|
+
const allScenarios = [...scenarioResult.scenarios, ...state.accumulatedProbes];
|
|
779
|
+
const { corrections, handwrittenWarnings } = extractScenarioCorrections(attributedFailures, allScenarios);
|
|
780
|
+
for (const warning of handwrittenWarnings) {
|
|
781
|
+
reporter.warn(` ${chalk.yellow('Warning:')} ${warning}`);
|
|
782
|
+
}
|
|
783
|
+
if (corrections.length > 0) {
|
|
784
|
+
scenarioResult.scenarios = applyScenarioCorrections(scenarioResult.scenarios, corrections);
|
|
785
|
+
const correctedProbes = applyScenarioCorrections(state.accumulatedProbes, corrections);
|
|
786
|
+
state.accumulatedProbes.splice(0, state.accumulatedProbes.length, ...correctedProbes);
|
|
787
|
+
reporter.warn(` ${chalk.dim(`Corrected ${corrections.length} scenario expectation(s)`)}`);
|
|
788
|
+
}
|
|
789
|
+
const attributionByDescription = new Map(attributedFailures.map((a) => [a.scenarioDescription, a]));
|
|
790
|
+
const ruleBlamedFailures = state.verificationResult.failedScenarios.filter((f) => {
|
|
791
|
+
const attr = attributionByDescription.get(f.scenario.description);
|
|
792
|
+
if (!attr || attr.blame.kind === 'rule' || attr.blame.kind === 'both')
|
|
793
|
+
return true;
|
|
794
|
+
return handwrittenWarnings.some((w) => w.includes(f.scenario.description));
|
|
795
|
+
});
|
|
796
|
+
// Clean exit: rules are correct (already passing, or only scenario-blame
|
|
797
|
+
// failures that we just corrected). Synthesize a passing result when
|
|
798
|
+
// corrections resolved the remaining failures.
|
|
799
|
+
if (state.verificationResult.pass)
|
|
800
|
+
break;
|
|
801
|
+
if (ruleBlamedFailures.length === 0) {
|
|
582
802
|
if (corrections.length > 0) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const allRuleBlamedFailures = verificationResult.failedScenarios.filter((f) => {
|
|
590
|
-
const attr = attributedFailures.find((a) => a.scenarioDescription === f.scenario.description);
|
|
591
|
-
if (!attr || attr.blame.kind === 'rule' || attr.blame.kind === 'both')
|
|
592
|
-
return true;
|
|
593
|
-
return handwrittenWarnings.some((w) => w.includes(f.scenario.description));
|
|
594
|
-
});
|
|
595
|
-
if (allRuleBlamedFailures.length > 0) {
|
|
596
|
-
const repairContext = {
|
|
597
|
-
failedScenarios: allRuleBlamedFailures,
|
|
598
|
-
judgeAnalysis,
|
|
599
|
-
attemptNumber: attempt,
|
|
600
|
-
existingListDefinitions: listDefinitions.length > 0 ? listDefinitions : undefined,
|
|
601
|
-
handwrittenScenarios: unit.handwrittenScenarios.length > 0 ? unit.handwrittenScenarios : undefined,
|
|
803
|
+
const correctionNote = `\n\n[pipeline-runner: cleared ${corrections.length} scenario expectation(s) via judge attribution; no rule-blamed failures remained.]`;
|
|
804
|
+
state.verificationResult = {
|
|
805
|
+
...state.verificationResult,
|
|
806
|
+
pass: true,
|
|
807
|
+
failedScenarios: [],
|
|
808
|
+
summary: `${state.verificationResult.summary}${correctionNote}`,
|
|
602
809
|
};
|
|
603
|
-
this.logContext.stepName = `repair-compile-${unit.serverName}-${attempt}`;
|
|
604
|
-
const repairText = ` ${unit.serverName} repair ${attempt}/${MAX_REPAIRS}: Recompiling`;
|
|
605
|
-
const { result: repairResult } = await withSpinner(repairText, async (spinner) => this.compilePolicyRulesWithPointFix(rules, unit.annotations, unit.protectedPaths, baseInputHash, repairContext, compilerSystem, compilerSession, listDefinitions, (msg) => {
|
|
606
|
-
spinner.text = `${repairText} — ${msg}`;
|
|
607
|
-
}), (r, elapsed) => `${repairText}: ${r.rules.length} rules (${elapsed.toFixed(1)}s)`);
|
|
608
|
-
rules = repairResult.rules;
|
|
609
|
-
listDefinitions = repairResult.listDefinitions;
|
|
610
|
-
compilerSession = repairResult.session;
|
|
611
|
-
// Re-validate server scoping after repair
|
|
612
|
-
validateServerScoping(unit.serverName, rules);
|
|
613
|
-
// Re-resolve dynamic lists if repair changed list definitions
|
|
614
|
-
if (listDefinitions.length > 0) {
|
|
615
|
-
dynamicLists = await this.resolveServerLists(listDefinitions, serverOutputDir, config, ` ${unit.serverName}`);
|
|
616
|
-
}
|
|
617
|
-
serverPolicyFile = buildPolicyArtifact(constitutionHash, rules, listDefinitions, inputHash);
|
|
618
|
-
// Persist repaired rules immediately
|
|
619
|
-
writeServerPolicy();
|
|
620
|
-
verifierSystem = this.cacheStrategy.wrapSystemPrompt(buildJudgeSystemPrompt(unit.constitutionText, serverPolicyFile, unit.protectedPaths, serverTools, dynamicLists, unit.allowedDirectory, unit.storedAnnotations));
|
|
621
|
-
verifierSession = new PolicyVerifierSession({
|
|
622
|
-
system: verifierSystem,
|
|
623
|
-
model: this.model,
|
|
624
|
-
serverNames,
|
|
625
|
-
toolNames: serverToolNames,
|
|
626
|
-
storedAnnotations: unit.storedAnnotations,
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
console.error(` ${chalk.dim('No rule-blamed failures — skipping recompilation')}`);
|
|
631
|
-
}
|
|
632
|
-
this.logContext.stepName = `repair-verify-${unit.serverName}-${attempt}`;
|
|
633
|
-
const scenariosForRepairVerify = [...currentFilteredScenarios, ...accumulatedProbes];
|
|
634
|
-
const repairVerifyText = ` ${unit.serverName} repair ${attempt}/${MAX_REPAIRS}: Verifying`;
|
|
635
|
-
const { result: repairVerifyResult } = await withSpinner(repairVerifyText, async (spinner) => verifyPolicy(unit.constitutionText, serverPolicyFile, serverAnnotationsFile, unit.protectedPaths, scenariosForRepairVerify, this.model, 1, unit.allowedDirectory, (msg) => {
|
|
636
|
-
spinner.text = `${repairVerifyText} — ${msg}`;
|
|
637
|
-
}, serverDomainAllowlists, dynamicLists, verifierSystem, verifierSession, unit.storedAnnotations), (r, elapsed) => r.pass
|
|
638
|
-
? `${repairVerifyText}: passed (${elapsed.toFixed(1)}s)`
|
|
639
|
-
: `${repairVerifyText}: ${r.failedScenarios.length} failure(s) (${elapsed.toFixed(1)}s)`);
|
|
640
|
-
verificationResult = repairVerifyResult;
|
|
641
|
-
if (!verificationResult.pass) {
|
|
642
|
-
this.logVerboseFailures(verificationResult);
|
|
643
|
-
}
|
|
644
|
-
const { valid: validRepairProbes } = filterAndLogStructuralConflicts(filterEngine, collectProbeScenarios(verificationResult), 'Discarded probe (structural conflict)', unit.storedAnnotations);
|
|
645
|
-
accumulatedProbes.push(...validRepairProbes);
|
|
646
|
-
repairAttempts = attempt;
|
|
647
|
-
if (verificationResult.pass) {
|
|
648
|
-
break;
|
|
649
810
|
}
|
|
811
|
+
break;
|
|
650
812
|
}
|
|
813
|
+
// Out of repair budget — exit with the current (failing) result.
|
|
814
|
+
if (attempt === MAX_REPAIRS)
|
|
815
|
+
break;
|
|
816
|
+
const repairAttempt = attempt + 1;
|
|
817
|
+
const { valid: currentFilteredScenarios } = filterAndLogStructuralConflicts(filterEngine, scenarioResult.scenarios, undefined, unit.storedAnnotations, reporter);
|
|
818
|
+
const repairContext = {
|
|
819
|
+
failedScenarios: ruleBlamedFailures,
|
|
820
|
+
judgeAnalysis,
|
|
821
|
+
attemptNumber: repairAttempt,
|
|
822
|
+
existingListDefinitions: state.listDefinitions.length > 0 ? state.listDefinitions : undefined,
|
|
823
|
+
handwrittenScenarios: unit.handwrittenScenarios.length > 0 ? unit.handwrittenScenarios : undefined,
|
|
824
|
+
};
|
|
825
|
+
logContext.stepName = `repair-compile-${unit.serverName}-${repairAttempt}`;
|
|
826
|
+
reporter.update('repair-compile', `attempt ${repairAttempt}/${MAX_REPAIRS}`);
|
|
827
|
+
const repairCompileStart = Date.now();
|
|
828
|
+
const repairResult = await this.compilePolicyRulesWithPointFix(state.rules, unit.storedAnnotations, unit.protectedPaths, inputHash, repairContext, compilerSystem, state.compilerSession, state.listDefinitions, model, (msg) => reporter.update('repair-compile', msg), reporter);
|
|
829
|
+
const repairCompileElapsed = (Date.now() - repairCompileStart) / 1000;
|
|
830
|
+
reporter.complete('repair-compile', ` ${unit.serverName} repair ${repairAttempt}/${MAX_REPAIRS}: ${repairResult.rules.length} rules (${repairCompileElapsed.toFixed(1)}s)`, repairCompileElapsed);
|
|
831
|
+
state.rules = repairResult.rules;
|
|
832
|
+
state.listDefinitions = repairResult.listDefinitions;
|
|
833
|
+
state.compilerSession = repairResult.session;
|
|
834
|
+
validateServerScoping(unit.serverName, state.rules);
|
|
835
|
+
if (state.listDefinitions.length > 0) {
|
|
836
|
+
state.dynamicLists = await this.resolveServerLists(state.listDefinitions, serverOutputDir, config, ` ${unit.serverName}`, model, logContext, reporter);
|
|
837
|
+
}
|
|
838
|
+
state.serverPolicyFile = buildPolicyArtifact(constitutionHash, state.rules, state.listDefinitions, inputHash);
|
|
839
|
+
writeServerPolicy();
|
|
840
|
+
const verifierSystem = this.cacheStrategy.wrapSystemPrompt(buildJudgeSystemPrompt(unit.constitutionText, state.serverPolicyFile, unit.protectedPaths, serverTools, state.dynamicLists, unit.allowedDirectory, unit.storedAnnotations));
|
|
841
|
+
const verifierSession = new PolicyVerifierSession({
|
|
842
|
+
system: verifierSystem,
|
|
843
|
+
model,
|
|
844
|
+
serverNames,
|
|
845
|
+
toolNames: serverToolNames,
|
|
846
|
+
storedAnnotations: unit.storedAnnotations,
|
|
847
|
+
});
|
|
848
|
+
logContext.stepName = `repair-verify-${unit.serverName}-${repairAttempt}`;
|
|
849
|
+
const scenariosForRepairVerify = [...currentFilteredScenarios, ...state.accumulatedProbes];
|
|
850
|
+
reporter.update('repair-verify', `attempt ${repairAttempt}/${MAX_REPAIRS}`);
|
|
851
|
+
const repairVerifyStart = Date.now();
|
|
852
|
+
const repairVerifyResult = await verifyPolicy(unit.constitutionText, state.serverPolicyFile, serverAnnotationsFile, unit.protectedPaths, scenariosForRepairVerify, model, 1, unit.allowedDirectory, (msg) => reporter.update('repair-verify', msg), serverDomainAllowlists, state.dynamicLists, verifierSystem, verifierSession, unit.storedAnnotations);
|
|
853
|
+
const repairVerifyElapsed = (Date.now() - repairVerifyStart) / 1000;
|
|
854
|
+
const repairVerifyText = ` ${unit.serverName} repair ${repairAttempt}/${MAX_REPAIRS}: Verifying`;
|
|
855
|
+
reporter.complete('repair-verify', repairVerifyResult.pass
|
|
856
|
+
? `${repairVerifyText}: passed (${repairVerifyElapsed.toFixed(1)}s)`
|
|
857
|
+
: `${repairVerifyText}: ${repairVerifyResult.failedScenarios.length} failure(s) (${repairVerifyElapsed.toFixed(1)}s)`, repairVerifyElapsed);
|
|
858
|
+
state.verificationResult = repairVerifyResult;
|
|
859
|
+
if (!state.verificationResult.pass) {
|
|
860
|
+
this.logVerboseFailures(state.verificationResult, reporter);
|
|
861
|
+
}
|
|
862
|
+
const { valid: validRepairProbes } = filterAndLogStructuralConflicts(filterEngine, collectProbeScenarios(state.verificationResult), 'Discarded probe (structural conflict)', unit.storedAnnotations, reporter);
|
|
863
|
+
state.accumulatedProbes.push(...validRepairProbes);
|
|
864
|
+
state.repairAttempts = repairAttempt;
|
|
651
865
|
}
|
|
652
|
-
// Write per-server artifacts (before verification check so they can be inspected on failure)
|
|
653
|
-
const finalScenarios = [...scenarioResult.scenarios, ...accumulatedProbes];
|
|
654
|
-
writeServerPolicy();
|
|
655
|
-
writeArtifact(serverOutputDir, 'test-scenarios.json', {
|
|
656
|
-
generatedAt: new Date().toISOString(),
|
|
657
|
-
constitutionHash,
|
|
658
|
-
inputHash: scenarioResult.inputHash,
|
|
659
|
-
scenarios: finalScenarios,
|
|
660
|
-
});
|
|
661
|
-
if (!verificationResult.pass) {
|
|
662
|
-
throw new Error(`Verification FAILED for server "${unit.serverName}" — artifacts written for inspection but policy may need review.`);
|
|
663
|
-
}
|
|
664
|
-
console.error(` ${chalk.green(unit.serverName)}: ${rules.length} rules, ${finalScenarios.length} scenarios` +
|
|
665
|
-
(repairAttempts > 0 ? `, ${repairAttempts} repair(s)` : ''));
|
|
666
|
-
return {
|
|
667
|
-
serverName: unit.serverName,
|
|
668
|
-
rules,
|
|
669
|
-
listDefinitions,
|
|
670
|
-
scenarios: finalScenarios,
|
|
671
|
-
inputHash,
|
|
672
|
-
constitutionHash,
|
|
673
|
-
resolvedLists: dynamicLists,
|
|
674
|
-
};
|
|
675
866
|
}
|
|
676
867
|
/**
|
|
677
868
|
* Compiles policy rules for a single server (initial compilation).
|
|
678
869
|
* Uses per-server schema with requireServer: true.
|
|
679
870
|
*/
|
|
680
|
-
async compileServerPolicyRules(unit, system, inputHash) {
|
|
681
|
-
|
|
871
|
+
async compileServerPolicyRules(unit, system, inputHash, model, reporter) {
|
|
872
|
+
reporter.update('compiling');
|
|
873
|
+
const start = Date.now();
|
|
682
874
|
const session = new ConstitutionCompilerSession({
|
|
683
875
|
system,
|
|
684
|
-
model
|
|
685
|
-
annotations: unit.
|
|
876
|
+
model,
|
|
877
|
+
annotations: unit.storedAnnotations,
|
|
686
878
|
schemaOptions: { requireServer: true },
|
|
687
879
|
});
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
validateRulesOrThrow(compiledRules, output.listDefinitions);
|
|
694
|
-
return { rules: compiledRules, listDefinitions: output.listDefinitions };
|
|
695
|
-
}, (output, elapsed) => `${stepText}: ${output.rules.length} rules (${elapsed.toFixed(1)}s)`);
|
|
880
|
+
const output = await session.compile((msg) => reporter.update('compiling', msg));
|
|
881
|
+
const compiledRules = resolveRulePaths(output.rules);
|
|
882
|
+
validateRulesOrThrow(compiledRules, output.listDefinitions, reporter);
|
|
883
|
+
const elapsed = (Date.now() - start) / 1000;
|
|
884
|
+
reporter.complete('compiling', ` ${unit.serverName}: Compiling rules: ${compiledRules.length} rules (${elapsed.toFixed(1)}s)`, elapsed);
|
|
696
885
|
return {
|
|
697
|
-
rules:
|
|
698
|
-
listDefinitions:
|
|
886
|
+
rules: compiledRules,
|
|
887
|
+
listDefinitions: output.listDefinitions,
|
|
699
888
|
inputHash,
|
|
700
889
|
session,
|
|
701
890
|
};
|
|
@@ -703,7 +892,7 @@ export class PipelineRunner {
|
|
|
703
892
|
// -----------------------------------------------------------------------
|
|
704
893
|
// Private compilation methods
|
|
705
894
|
// -----------------------------------------------------------------------
|
|
706
|
-
async compilePolicyRulesWithRepair(annotations, protectedPaths, baseInputHash, repairContext, system, session, onProgress) {
|
|
895
|
+
async compilePolicyRulesWithRepair(annotations, protectedPaths, baseInputHash, repairContext, system, session, model, onProgress, reporter) {
|
|
707
896
|
let output;
|
|
708
897
|
if (session) {
|
|
709
898
|
output = await session.recompile(repairContext, onProgress);
|
|
@@ -711,10 +900,10 @@ export class PipelineRunner {
|
|
|
711
900
|
else {
|
|
712
901
|
const { compileConstitution } = await import('./constitution-compiler.js');
|
|
713
902
|
output = await compileConstitution('', // Not needed when system prompt is provided
|
|
714
|
-
annotations, { protectedPaths },
|
|
903
|
+
annotations, { protectedPaths }, model, repairContext, onProgress, system);
|
|
715
904
|
}
|
|
716
905
|
const compiledRules = resolveRulePaths(output.rules);
|
|
717
|
-
validateRulesOrThrow(compiledRules, output.listDefinitions);
|
|
906
|
+
validateRulesOrThrow(compiledRules, output.listDefinitions, reporter);
|
|
718
907
|
return {
|
|
719
908
|
rules: compiledRules,
|
|
720
909
|
listDefinitions: output.listDefinitions,
|
|
@@ -728,17 +917,17 @@ export class PipelineRunner {
|
|
|
728
917
|
* patch instead of regenerating the entire rule set. This avoids oscillation
|
|
729
918
|
* where fixing one failure breaks previously-passing rules.
|
|
730
919
|
*/
|
|
731
|
-
async compilePolicyRulesWithPointFix(existingRules, annotations, protectedPaths, baseInputHash, repairContext, system, session, existingListDefinitions, onProgress) {
|
|
920
|
+
async compilePolicyRulesWithPointFix(existingRules, annotations, protectedPaths, baseInputHash, repairContext, system, session, existingListDefinitions, model, onProgress, reporter) {
|
|
732
921
|
let output;
|
|
733
922
|
if (session) {
|
|
734
923
|
output = await session.repairPointFix(existingRules, repairContext, existingListDefinitions, onProgress);
|
|
735
924
|
}
|
|
736
925
|
else {
|
|
737
926
|
// No session available -- fall back to full recompile
|
|
738
|
-
return this.compilePolicyRulesWithRepair(annotations, protectedPaths, baseInputHash, repairContext, system, session, onProgress);
|
|
927
|
+
return this.compilePolicyRulesWithRepair(annotations, protectedPaths, baseInputHash, repairContext, system, session, model, onProgress, reporter);
|
|
739
928
|
}
|
|
740
929
|
const compiledRules = resolveRulePaths(output.rules);
|
|
741
|
-
validateRulesOrThrow(compiledRules, output.listDefinitions);
|
|
930
|
+
validateRulesOrThrow(compiledRules, output.listDefinitions, reporter);
|
|
742
931
|
return {
|
|
743
932
|
rules: compiledRules,
|
|
744
933
|
listDefinitions: output.listDefinitions,
|
|
@@ -751,25 +940,24 @@ export class PipelineRunner {
|
|
|
751
940
|
* Loads existing per-server dynamic-lists.json for cache comparison,
|
|
752
941
|
* connects MCP servers if needed, and writes results to serverOutputDir.
|
|
753
942
|
*/
|
|
754
|
-
async resolveServerLists(listDefinitions, serverOutputDir, config, labelPrefix) {
|
|
755
|
-
|
|
943
|
+
async resolveServerLists(listDefinitions, serverOutputDir, config, labelPrefix, model, logContext, reporter) {
|
|
944
|
+
logContext.stepName = `resolve-lists-${labelPrefix.trim()}`;
|
|
756
945
|
const existingLists = loadExistingArtifact(serverOutputDir, 'dynamic-lists.json');
|
|
757
946
|
const needsMcp = listDefinitions.some((d) => d.requiresMcp);
|
|
758
947
|
let proxy;
|
|
759
948
|
if (needsMcp && config.mcpServers) {
|
|
760
949
|
proxy = await connectViaProxy(listDefinitions, config.mcpServers, config.toolAnnotationsDir);
|
|
761
950
|
}
|
|
762
|
-
|
|
951
|
+
reporter.update('lists');
|
|
952
|
+
const start = Date.now();
|
|
763
953
|
try {
|
|
764
|
-
const
|
|
765
|
-
model
|
|
954
|
+
const resolvedLists = await resolveAllLists(listDefinitions, {
|
|
955
|
+
model,
|
|
766
956
|
proxyConnection: proxy ? { client: proxy.client, tools: proxy.tools } : undefined,
|
|
767
|
-
}, existingLists, (msg) =>
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
return `${listStepText}: ${count} list(s) resolved (${elapsed.toFixed(1)}s)`;
|
|
772
|
-
});
|
|
957
|
+
}, existingLists, (msg) => reporter.update('lists', msg));
|
|
958
|
+
const elapsed = (Date.now() - start) / 1000;
|
|
959
|
+
const count = Object.keys(resolvedLists.lists).length;
|
|
960
|
+
reporter.complete('lists', `${labelPrefix}: Resolving dynamic lists: ${count} list(s) resolved (${elapsed.toFixed(1)}s)`, elapsed);
|
|
773
961
|
writeArtifact(serverOutputDir, 'dynamic-lists.json', resolvedLists);
|
|
774
962
|
return resolvedLists;
|
|
775
963
|
}
|
|
@@ -779,28 +967,28 @@ export class PipelineRunner {
|
|
|
779
967
|
}
|
|
780
968
|
}
|
|
781
969
|
}
|
|
782
|
-
async generateTestScenarios(constitutionText, annotations, allowedDirectory, handwrittenScenarios, inputHash, existingScenarios, stepLabel, permittedDirectories, dynamicLists,
|
|
970
|
+
async generateTestScenarios(constitutionText, annotations, allowedDirectory, handwrittenScenarios, inputHash, existingScenarios, stepLabel, permittedDirectories, dynamicLists, model, reporter) {
|
|
783
971
|
const stepText = `${stepLabel} Generating test scenarios`;
|
|
784
972
|
if (existingScenarios && existingScenarios.inputHash === inputHash) {
|
|
785
|
-
|
|
973
|
+
reporter.complete('scenarios', `${stepText} ${chalk.dim('(cached)')}`, 0);
|
|
786
974
|
return { scenarios: existingScenarios.scenarios, inputHash };
|
|
787
975
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
});
|
|
976
|
+
reporter.update('scenarios');
|
|
977
|
+
const start = Date.now();
|
|
978
|
+
const scenarios = await generateScenarios(constitutionText, annotations, handwrittenScenarios, allowedDirectory, model, permittedDirectories, (msg) => reporter.update('scenarios', msg), dynamicLists, (prompt) => this.cacheStrategy.wrapSystemPrompt(prompt));
|
|
979
|
+
const elapsed = (Date.now() - start) / 1000;
|
|
980
|
+
const generatedCount = scenarios.length - handwrittenScenarios.length;
|
|
981
|
+
reporter.complete('scenarios', `${stepText}: ${scenarios.length} scenarios (${handwrittenScenarios.length} handwritten + ${generatedCount} generated) (${elapsed.toFixed(1)}s)`, elapsed);
|
|
794
982
|
return { scenarios, inputHash };
|
|
795
983
|
}
|
|
796
|
-
logVerboseFailures(result) {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
984
|
+
logVerboseFailures(result, reporter) {
|
|
985
|
+
reporter.warn('');
|
|
986
|
+
reporter.warn(chalk.red('Verification FAILED:'));
|
|
987
|
+
reporter.warn(result.summary);
|
|
988
|
+
reporter.warn('');
|
|
801
989
|
for (const f of result.failedScenarios) {
|
|
802
|
-
|
|
803
|
-
|
|
990
|
+
reporter.warn(` ${chalk.red('FAIL:')} ${f.scenario.description}`);
|
|
991
|
+
reporter.warn(` Expected: ${f.scenario.expectedDecision}, Got: ${f.actualDecision} (rule: ${f.matchingRule})`);
|
|
804
992
|
}
|
|
805
993
|
}
|
|
806
994
|
}
|