@iowarp/clio-coder 0.3.8 → 0.3.9
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/CHANGELOG.md +49 -0
- package/README.md +7 -3
- package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
- package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
- package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
- package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
- package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
- package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
- package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
- package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
- package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
- package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
- package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
- package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
- package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
- package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
- package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
- package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
- package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
- package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
- package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
- package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
- package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
- package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
- package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
- package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
- package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
- package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
- package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
- package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
- package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
- package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
- package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
- package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
- package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
- package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
- package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
- package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
- package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
- package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
- package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
- package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +27 -27
- package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
- package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
- package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
- package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
- package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
- package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
- package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
- package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
- package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
- package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
- package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
- package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
- package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
- package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
- package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
- package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
- package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
- package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
- package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
- package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
- package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
- package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
- package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
- package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
- package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
- package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
- package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
- package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
- package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
- package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
- package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
- package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
- package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
- package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
- package/dist/worker/entry.js +29 -33
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +6 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +1 -1
- package/docs/configuration-and-targets.md +30 -3
- package/docs/context-engine.md +63 -4
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +2 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +11 -10
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +4 -4
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +53 -2
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +19 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +19 -3
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +87 -0
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +2 -1
- package/src/cli/agents.ts +1 -1
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +3 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet.ts +32 -3
- package/src/cli/targets.ts +44 -13
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/core/bus-events.ts +29 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/config.ts +18 -0
- package/src/core/defaults.ts +36 -6
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +11 -0
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/extension.ts +166 -42
- package/src/domains/dispatch/fleet-run.ts +23 -3
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +3 -0
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
- package/src/domains/dispatch/write-boundary.ts +62 -1
- package/src/domains/eval/artifacts/store.ts +62 -0
- package/src/domains/eval/compare/behavioral.ts +224 -0
- package/src/domains/eval/compare/compare.ts +355 -2
- package/src/domains/eval/compare/envelope.ts +128 -0
- package/src/domains/eval/compare/gates.ts +24 -6
- package/src/domains/eval/compare/thresholds.ts +30 -3
- package/src/domains/eval/execution-provenance.ts +240 -0
- package/src/domains/eval/metrics/aggregate.ts +136 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
- package/src/domains/eval/metrics/tracked.ts +413 -0
- package/src/domains/eval/provenance.ts +117 -0
- package/src/domains/eval/reports/comparison.ts +128 -0
- package/src/domains/eval/reports/junit.ts +17 -3
- package/src/domains/eval/reports/markdown.ts +3 -3
- package/src/domains/eval/reports/text.ts +14 -0
- package/src/domains/eval/run-compare.ts +20 -0
- package/src/domains/eval/runners/clio-run.ts +127 -0
- package/src/domains/eval/runners/external-command.ts +28 -3
- package/src/domains/eval/schema/adapter.ts +111 -0
- package/src/domains/eval/schema/artifact.ts +20 -0
- package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
- package/src/domains/eval/schema/behavioral.ts +520 -0
- package/src/domains/eval/schema/execution-envelope.ts +194 -0
- package/src/domains/eval/schema/serving.ts +74 -0
- package/src/domains/eval/schema/suite.ts +38 -8
- package/src/domains/eval/schema/validate.ts +58 -3
- package/src/domains/eval/schema/verdict.ts +237 -0
- package/src/domains/eval/suites/resolve.ts +2 -0
- package/src/domains/eval/suites/run.ts +264 -33
- package/src/domains/eval/verifiers/command.ts +2 -1
- package/src/domains/eval/workspaces/temp-copy.ts +145 -13
- package/src/domains/evidence/build.ts +2 -13
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +2 -2
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +38 -3
- package/src/domains/extensions/resources.ts +1 -1
- package/src/domains/extensions/state.ts +12 -3
- package/src/domains/extensions/types.ts +2 -0
- package/src/domains/lifecycle/doctor.ts +69 -1
- package/src/domains/memory/index.ts +14 -0
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +77 -8
- package/src/domains/memory/task-memory-spend.ts +131 -0
- package/src/domains/memory/task-memory-status.ts +7 -0
- package/src/domains/memory/task-memory-telemetry.ts +2 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +69 -5
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/index.ts +7 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +192 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/providers/endpoint-capacity.ts +96 -0
- package/src/domains/providers/index.ts +10 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
- package/src/domains/providers/runtime-resolution.ts +8 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/prompts/loader.ts +95 -33
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/run-effects.ts +35 -4
- package/src/domains/session/context-accounting.ts +52 -1
- package/src/domains/session/context-ledger.ts +37 -13
- package/src/domains/session/index.ts +6 -0
- package/src/domains/session/prompt-cache.ts +140 -0
- package/src/domains/session/prompt-manifest.ts +42 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/provider-payload.ts +29 -1
- package/src/entry/orchestrator.ts +176 -30
- package/src/interactive/chat-loop-messages.ts +26 -7
- package/src/interactive/chat-loop.ts +318 -41
- package/src/interactive/chat-panel.ts +62 -8
- package/src/interactive/clio-editor.ts +45 -8
- package/src/interactive/context-activity.ts +5 -1
- package/src/interactive/context-meter.ts +1 -1
- package/src/interactive/context-overlay.ts +40 -10
- package/src/interactive/cost-overlay.ts +64 -6
- package/src/interactive/dispatch-board.ts +84 -12
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +24 -1
- package/src/interactive/interactive-input-runtime.ts +8 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +27 -4
- package/src/interactive/memory-overlay.ts +8 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/overlay-general-openers.ts +16 -0
- package/src/interactive/overlay-key-routing.ts +38 -0
- package/src/interactive/overlay-lifecycle.ts +38 -5
- package/src/interactive/overlay-permission-lifecycle.ts +22 -2
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlays/ask-user.ts +91 -19
- package/src/interactive/overlays/help-reference.ts +4 -0
- package/src/interactive/overlays/prompts.ts +11 -1
- package/src/interactive/overlays/settings.ts +35 -1
- package/src/interactive/permission-hint.ts +34 -2
- package/src/interactive/permission-overlay.ts +159 -9
- package/src/interactive/prewarm.ts +197 -0
- package/src/interactive/render-trace.ts +162 -15
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/status/controller.ts +11 -0
- package/src/interactive/status/state-machine.ts +54 -2
- package/src/interactive/status/types.ts +7 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/turn-context.ts +299 -31
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/view-overlay.ts +28 -3
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-plan.ts +17 -9
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/registry.ts +16 -0
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/doctor-M7YEDGAE.js +0 -91
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { mkdtemp, rm } from "node:fs/promises";
|
|
1
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { resolveMetricAssertion } from "../compare/thresholds.js";
|
|
5
|
+
import { buildEvalExecutionEnvelopeV1, type EvalExecutionObservationV1 } from "../execution-provenance.js";
|
|
6
|
+
import { aggregateEvalVerdicts } from "../metrics/aggregate.js";
|
|
5
7
|
import { collectContextMetrics } from "../metrics/context.js";
|
|
6
8
|
import { fleetLoopReceiptAgreement } from "../metrics/fleet-loop-stream.js";
|
|
7
9
|
import {
|
|
@@ -15,12 +17,22 @@ import {
|
|
|
15
17
|
import { wallTimeMetric } from "../metrics/latency.js";
|
|
16
18
|
import { tokenAccountingFrom } from "../metrics/tokens.js";
|
|
17
19
|
import { zeroToolCallMetrics } from "../metrics/tool-calls.js";
|
|
18
|
-
import {
|
|
20
|
+
import { buildEvalTrackedMetrics, emptyEvalTrackedMetrics, readEvalLedgerSnapshot } from "../metrics/tracked.js";
|
|
21
|
+
import {
|
|
22
|
+
type EvalServingObservation,
|
|
23
|
+
evalClioProvenance,
|
|
24
|
+
evalEnvironmentProvenance,
|
|
25
|
+
evalServingConfiguration,
|
|
26
|
+
evalServingObservationFrom,
|
|
27
|
+
} from "../provenance.js";
|
|
19
28
|
import { runClioRunRunner } from "../runners/clio-run.js";
|
|
20
29
|
import { runContextIndexRunner } from "../runners/context-index.js";
|
|
21
30
|
import { runContextInitRunner } from "../runners/context-init.js";
|
|
22
31
|
import { type EvalRunnerOutput, runExternalCommandRunner } from "../runners/external-command.js";
|
|
32
|
+
import { adaptSuiteV2ResultToBehaviorV1, adaptSuiteV2ResultToVerdictV1 } from "../schema/adapter.js";
|
|
23
33
|
import type { EvalArtifactResultV4, EvalArtifactV4 } from "../schema/artifact.js";
|
|
34
|
+
import { buildEvalBehaviorMetricsV1 } from "../schema/behavioral-metrics.js";
|
|
35
|
+
import type { EvalServingConfigurationV1 } from "../schema/serving.js";
|
|
24
36
|
import type { EvalMetricAssertion, EvalSuiteTargetV2, LoadedEvalSuiteV2 } from "../schema/suite.js";
|
|
25
37
|
import { createEvalId } from "../store.js";
|
|
26
38
|
import { runCommandVerifiers } from "../verifiers/command.js";
|
|
@@ -28,7 +40,7 @@ import { forbiddenPathHits } from "../verifiers/file-exists.js";
|
|
|
28
40
|
import { collectPatchMetrics } from "../verifiers/patch.js";
|
|
29
41
|
import { prepareGitWorkspace } from "../workspaces/git.js";
|
|
30
42
|
import { type PreparedEvalWorkspace, prepareLocalWorkspace } from "../workspaces/local.js";
|
|
31
|
-
import { prepareTempCopyWorkspace } from "../workspaces/temp-copy.js";
|
|
43
|
+
import { type PrepareTempCopyWorkspaceOptions, prepareTempCopyWorkspace } from "../workspaces/temp-copy.js";
|
|
32
44
|
import { expandEvalMatrix } from "./matrix.js";
|
|
33
45
|
import { artifactMatrixIdentity } from "./resolve.js";
|
|
34
46
|
|
|
@@ -42,6 +54,15 @@ class EvalWorkspaceSetupError extends Error {
|
|
|
42
54
|
export interface RunEvalSuiteV2Options {
|
|
43
55
|
clioEntry: string;
|
|
44
56
|
now?: () => Date;
|
|
57
|
+
/** Convert local workspaces into isolated copies for explicit trial runs. */
|
|
58
|
+
freshWorkspaces?: boolean;
|
|
59
|
+
/** Dependency/root seam for deterministic workspace lifecycle contracts. */
|
|
60
|
+
tempCopy?: PrepareTempCopyWorkspaceOptions;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface CompletedMatrixItem {
|
|
64
|
+
result: EvalArtifactResultV4;
|
|
65
|
+
serving: EvalServingObservation;
|
|
45
66
|
}
|
|
46
67
|
|
|
47
68
|
export async function runEvalSuiteV2(
|
|
@@ -52,6 +73,7 @@ export async function runEvalSuiteV2(
|
|
|
52
73
|
const started = now();
|
|
53
74
|
const evalId = createEvalId(started, loaded.hash);
|
|
54
75
|
const results: EvalArtifactResultV4[] = [];
|
|
76
|
+
const servingObservations: EvalServingObservation[] = [];
|
|
55
77
|
const maxCostUsd = loaded.suite.matrix.maxCostUsd;
|
|
56
78
|
let spentUsd = 0;
|
|
57
79
|
for (const item of expandEvalMatrix(loaded.suite)) {
|
|
@@ -59,14 +81,24 @@ export async function runEvalSuiteV2(
|
|
|
59
81
|
// exceeds it, remaining items fail closed instead of running. A live
|
|
60
82
|
// suite can therefore never keep spending past its declared budget.
|
|
61
83
|
if (maxCostUsd !== undefined && spentUsd > maxCostUsd) {
|
|
62
|
-
results.push(budgetExhaustedResult(item.task
|
|
84
|
+
results.push(budgetExhaustedResult(loaded, item.task, item.target, item.repeatIndex, spentUsd, maxCostUsd));
|
|
63
85
|
continue;
|
|
64
86
|
}
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
87
|
+
const completed = await runMatrixItem(
|
|
88
|
+
loaded,
|
|
89
|
+
item.task,
|
|
90
|
+
item.target,
|
|
91
|
+
item.repeatIndex,
|
|
92
|
+
options.clioEntry,
|
|
93
|
+
options.freshWorkspaces === true,
|
|
94
|
+
options.tempCopy,
|
|
95
|
+
);
|
|
96
|
+
spentUsd += resultCostUsd(completed.result);
|
|
97
|
+
results.push(completed.result);
|
|
98
|
+
servingObservations.push(completed.serving);
|
|
68
99
|
}
|
|
69
|
-
|
|
100
|
+
const serving = await evalServingConfiguration(loaded.suite.matrix.targets, servingObservations);
|
|
101
|
+
return buildArtifact(loaded, evalId, results, options.clioEntry, serving);
|
|
70
102
|
}
|
|
71
103
|
|
|
72
104
|
/** Known receipt cost of one finished matrix item; unpriced runs count zero. */
|
|
@@ -76,16 +108,17 @@ export function resultCostUsd(result: Pick<EvalArtifactResultV4, "metrics">): nu
|
|
|
76
108
|
}
|
|
77
109
|
|
|
78
110
|
function budgetExhaustedResult(
|
|
79
|
-
|
|
111
|
+
loaded: LoadedEvalSuiteV2,
|
|
112
|
+
task: LoadedEvalSuiteV2["suite"]["tasks"][number],
|
|
80
113
|
target: EvalSuiteTargetV2,
|
|
81
114
|
repeatIndex: number,
|
|
82
115
|
spentUsd: number,
|
|
83
116
|
maxCostUsd: number,
|
|
84
117
|
): EvalArtifactResultV4 {
|
|
85
|
-
|
|
118
|
+
const result: EvalArtifactResultV4 = {
|
|
86
119
|
assignmentId: null,
|
|
87
120
|
terminalReceiptDigest: null,
|
|
88
|
-
taskId,
|
|
121
|
+
taskId: task.id,
|
|
89
122
|
repeatIndex,
|
|
90
123
|
target: { id: target.id, model: target.model ?? null, thinking: target.thinking ?? null },
|
|
91
124
|
pass: false,
|
|
@@ -100,6 +133,10 @@ function budgetExhaustedResult(
|
|
|
100
133
|
error: `matrix cost budget exhausted: spent $${spentUsd.toFixed(4)} of max $${maxCostUsd.toFixed(4)} before this item`,
|
|
101
134
|
},
|
|
102
135
|
};
|
|
136
|
+
result.verdict = adaptSuiteV2ResultToVerdictV1(result, emptyEvalTrackedMetrics());
|
|
137
|
+
attachBehavioralResult(result, task);
|
|
138
|
+
attachExecutionEnvelope(result, task, target, loaded.baseDir, null, emptyLedgerSnapshot());
|
|
139
|
+
return result;
|
|
103
140
|
}
|
|
104
141
|
|
|
105
142
|
async function runMatrixItem(
|
|
@@ -108,15 +145,20 @@ async function runMatrixItem(
|
|
|
108
145
|
target: EvalSuiteTargetV2,
|
|
109
146
|
repeatIndex: number,
|
|
110
147
|
clioEntry: string,
|
|
111
|
-
|
|
148
|
+
freshWorkspace: boolean,
|
|
149
|
+
tempCopy: PrepareTempCopyWorkspaceOptions | undefined,
|
|
150
|
+
): Promise<CompletedMatrixItem> {
|
|
112
151
|
let workspace: PreparedEvalWorkspace | null = null;
|
|
152
|
+
let receipt: EvalRunnerOutput["receipt"] = null;
|
|
153
|
+
let runnerWallTimeMs = 0;
|
|
154
|
+
let executionObservation: EvalExecutionObservationV1 | undefined;
|
|
113
155
|
// One matrix item, one Clio journal. An item measures Clio, and a shared
|
|
114
156
|
// state directory would mix sibling processes' runs and yesterday's sessions
|
|
115
157
|
// into the reading; pinning it here also means an item leaves nothing behind
|
|
116
158
|
// in the operator's own state.
|
|
117
|
-
const stateDir = await mkdtemp(resolve(tmpdir(), "clio-eval-state-"));
|
|
159
|
+
const stateDir = await mkdtemp(resolve(tempCopy?.tempRoot ?? tmpdir(), "clio-eval-state-"));
|
|
118
160
|
try {
|
|
119
|
-
workspace = await prepareWorkspace(loaded.baseDir, task);
|
|
161
|
+
workspace = await prepareWorkspace(loaded.baseDir, task, freshWorkspace, tempCopy);
|
|
120
162
|
const setup = await runCommandVerifiers(task.workspace.setup ?? [], workspace.dir, task.timeoutMs);
|
|
121
163
|
// A fixture that never came up measured nothing, so the item fails as a
|
|
122
164
|
// harness failure rather than reporting an invariant it never observed.
|
|
@@ -125,12 +167,20 @@ async function runMatrixItem(
|
|
|
125
167
|
CLIO_CODER_STATE_DIR: stateDir,
|
|
126
168
|
CLIO_CODER_ENTRY: clioEntry,
|
|
127
169
|
});
|
|
170
|
+
const runnerStdoutFile = resolve(stateDir, "eval-runner-output.jsonl");
|
|
171
|
+
await writeFile(runnerStdoutFile, runner.stdout, "utf8");
|
|
172
|
+
receipt = runner.receipt ?? null;
|
|
173
|
+
runnerWallTimeMs = runner.wallTimeMs;
|
|
128
174
|
const patch = collectPatchMetrics(workspace.dir);
|
|
129
175
|
const receiptExitCode = runner.exitCode;
|
|
130
176
|
// Read after the runner returned and before the journal is removed: what
|
|
131
177
|
// Clio sealed for this item, judged against its own ledger, and whether
|
|
132
178
|
// the workers it attested are still running.
|
|
133
179
|
const journalMetrics = invariantMetrics(stateDir, receiptExitCode);
|
|
180
|
+
const measurement = await measureTaskOutcome(task, workspace.dir, {
|
|
181
|
+
CLIO_EVAL_RUNNER_STDOUT_FILE: runnerStdoutFile,
|
|
182
|
+
});
|
|
183
|
+
executionObservation = measurement.executionObservation;
|
|
134
184
|
const metrics: Record<string, number | string | boolean | null> = {
|
|
135
185
|
...zeroToolCallMetrics(),
|
|
136
186
|
...collectContextMetrics(workspace.dir),
|
|
@@ -146,15 +196,22 @@ async function runMatrixItem(
|
|
|
146
196
|
"patch.testFilesModified": patch.testFilesModified,
|
|
147
197
|
"result.pass": runner.exitCode === 0,
|
|
148
198
|
"result.failureClass": runner.exitCode === 0 ? null : "runner_failed",
|
|
149
|
-
...
|
|
199
|
+
...measurement.metrics,
|
|
150
200
|
};
|
|
151
201
|
const verifier = await runVerifiers(task, workspace.dir, metrics);
|
|
152
|
-
const
|
|
153
|
-
const
|
|
202
|
+
const graderFailed = metrics["task.solved"] === false;
|
|
203
|
+
const pass = runner.exitCode === 0 && verifier.pass && !graderFailed;
|
|
204
|
+
const failureClass = pass
|
|
205
|
+
? null
|
|
206
|
+
: runner.exitCode !== 0
|
|
207
|
+
? "runner_failed"
|
|
208
|
+
: !verifier.pass
|
|
209
|
+
? verifier.failureClass
|
|
210
|
+
: "grader_failed";
|
|
154
211
|
metrics["verifier.exitCode"] = verifier.exitCode;
|
|
155
212
|
metrics["result.pass"] = pass;
|
|
156
213
|
metrics["result.failureClass"] = failureClass;
|
|
157
|
-
|
|
214
|
+
const result: EvalArtifactResultV4 = {
|
|
158
215
|
assignmentId: runner.assignmentId,
|
|
159
216
|
terminalReceiptDigest: runner.terminalReceiptDigest,
|
|
160
217
|
taskId: task.id,
|
|
@@ -165,13 +222,30 @@ async function runMatrixItem(
|
|
|
165
222
|
metrics,
|
|
166
223
|
artifacts: {
|
|
167
224
|
...runner.artifacts,
|
|
225
|
+
workspace: workspace.dir,
|
|
168
226
|
...(verifier.stdout.length > 0 ? { verifierStdout: verifier.stdout } : {}),
|
|
169
227
|
...(verifier.stderr.length > 0 ? { verifierStderr: verifier.stderr } : {}),
|
|
170
228
|
},
|
|
171
229
|
};
|
|
230
|
+
const snapshot = await readEvalLedgerSnapshot(stateDir);
|
|
231
|
+
const ledgerEntries = [...snapshot.entries, ...(runner.ledgerEntries ?? [])];
|
|
232
|
+
result.verdict = adaptSuiteV2ResultToVerdictV1(
|
|
233
|
+
result,
|
|
234
|
+
buildEvalTrackedMetrics({
|
|
235
|
+
ledgerEntries,
|
|
236
|
+
receipt: receipt ?? null,
|
|
237
|
+
fallbackWallClockMs: runner.wallTimeMs,
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
240
|
+
attachBehavioralResult(result, task);
|
|
241
|
+
attachExecutionEnvelope(result, task, target, workspace.dir, receipt ?? null, snapshot, executionObservation);
|
|
242
|
+
return {
|
|
243
|
+
result,
|
|
244
|
+
serving: evalServingObservationFrom(target, receipt ?? null, snapshot.compiledPromptHashes),
|
|
245
|
+
};
|
|
172
246
|
} catch (error) {
|
|
173
247
|
const failureClass = error instanceof EvalWorkspaceSetupError ? "setup_failed" : "command_error";
|
|
174
|
-
|
|
248
|
+
const result: EvalArtifactResultV4 = {
|
|
175
249
|
assignmentId: null,
|
|
176
250
|
terminalReceiptDigest: null,
|
|
177
251
|
taskId: task.id,
|
|
@@ -185,14 +259,74 @@ async function runMatrixItem(
|
|
|
185
259
|
"verifier.exitCode": 1,
|
|
186
260
|
"latency.wallMs": 0,
|
|
187
261
|
},
|
|
188
|
-
artifacts: {
|
|
262
|
+
artifacts: {
|
|
263
|
+
error: error instanceof Error ? error.message : String(error),
|
|
264
|
+
...(workspace === null ? {} : { workspace: workspace.dir }),
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
const snapshot = await readEvalLedgerSnapshot(stateDir);
|
|
268
|
+
result.verdict = adaptSuiteV2ResultToVerdictV1(
|
|
269
|
+
result,
|
|
270
|
+
buildEvalTrackedMetrics({
|
|
271
|
+
ledgerEntries: snapshot.entries,
|
|
272
|
+
receipt: receipt ?? null,
|
|
273
|
+
fallbackWallClockMs: runnerWallTimeMs,
|
|
274
|
+
}),
|
|
275
|
+
);
|
|
276
|
+
attachBehavioralResult(result, task);
|
|
277
|
+
attachExecutionEnvelope(
|
|
278
|
+
result,
|
|
279
|
+
task,
|
|
280
|
+
target,
|
|
281
|
+
workspace?.dir ?? loaded.baseDir,
|
|
282
|
+
receipt ?? null,
|
|
283
|
+
snapshot,
|
|
284
|
+
executionObservation,
|
|
285
|
+
);
|
|
286
|
+
return {
|
|
287
|
+
result,
|
|
288
|
+
serving: evalServingObservationFrom(target, receipt ?? null, snapshot.compiledPromptHashes),
|
|
189
289
|
};
|
|
190
290
|
} finally {
|
|
191
|
-
|
|
192
|
-
|
|
291
|
+
try {
|
|
292
|
+
await workspace?.cleanup();
|
|
293
|
+
} finally {
|
|
294
|
+
// A workspace cleanup error must not strand the suite-owned journal.
|
|
295
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
296
|
+
}
|
|
193
297
|
}
|
|
194
298
|
}
|
|
195
299
|
|
|
300
|
+
function attachBehavioralResult(result: EvalArtifactResultV4, task: LoadedEvalSuiteV2["suite"]["tasks"][number]): void {
|
|
301
|
+
if (task.behavioral === undefined || result.verdict === undefined) return;
|
|
302
|
+
result.behavioral = adaptSuiteV2ResultToBehaviorV1(result, result.verdict, task.behavioral);
|
|
303
|
+
result.behavioralMetrics = buildEvalBehaviorMetricsV1(result, task.behavioral.execution.subject.role);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function attachExecutionEnvelope(
|
|
307
|
+
result: EvalArtifactResultV4,
|
|
308
|
+
task: LoadedEvalSuiteV2["suite"]["tasks"][number],
|
|
309
|
+
target: EvalSuiteTargetV2,
|
|
310
|
+
cwd: string | null,
|
|
311
|
+
receipt: NonNullable<EvalRunnerOutput["receipt"]> | null,
|
|
312
|
+
ledger: Awaited<ReturnType<typeof readEvalLedgerSnapshot>>,
|
|
313
|
+
observation?: EvalExecutionObservationV1,
|
|
314
|
+
): void {
|
|
315
|
+
if (task.behavioral === undefined) return;
|
|
316
|
+
result.executionEnvelope = buildEvalExecutionEnvelopeV1({
|
|
317
|
+
task,
|
|
318
|
+
target,
|
|
319
|
+
cwd,
|
|
320
|
+
receipt,
|
|
321
|
+
ledger,
|
|
322
|
+
...(observation === undefined ? {} : { observation }),
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function emptyLedgerSnapshot(): Awaited<ReturnType<typeof readEvalLedgerSnapshot>> {
|
|
327
|
+
return { entries: [], compiledPromptHashes: [], promptManifests: [], contextSnapshots: [] };
|
|
328
|
+
}
|
|
329
|
+
|
|
196
330
|
/** Journal-derived invariants for one finished item, read from its isolated state directory. */
|
|
197
331
|
function invariantMetrics(stateDir: string, runnerExitCode: number): Record<string, number | boolean> {
|
|
198
332
|
const journal = readRunJournal(stateDir);
|
|
@@ -208,10 +342,15 @@ function invariantMetrics(stateDir: string, runnerExitCode: number): Record<stri
|
|
|
208
342
|
async function prepareWorkspace(
|
|
209
343
|
baseDir: string,
|
|
210
344
|
task: LoadedEvalSuiteV2["suite"]["tasks"][number],
|
|
345
|
+
freshWorkspace: boolean,
|
|
346
|
+
tempCopy: PrepareTempCopyWorkspaceOptions | undefined,
|
|
211
347
|
): Promise<PreparedEvalWorkspace> {
|
|
348
|
+
if (task.workspace.kind === "local" && freshWorkspace) {
|
|
349
|
+
return prepareTempCopyWorkspace(baseDir, { ...task.workspace, kind: "temp-copy" }, tempCopy);
|
|
350
|
+
}
|
|
212
351
|
if (task.workspace.kind === "local") return prepareLocalWorkspace(baseDir, task.workspace);
|
|
213
352
|
if (task.workspace.kind === "git") return prepareGitWorkspace(task.workspace);
|
|
214
|
-
return prepareTempCopyWorkspace(baseDir, task.workspace);
|
|
353
|
+
return prepareTempCopyWorkspace(baseDir, task.workspace, tempCopy);
|
|
215
354
|
}
|
|
216
355
|
|
|
217
356
|
async function runTaskRunner(
|
|
@@ -225,23 +364,87 @@ async function runTaskRunner(
|
|
|
225
364
|
if (task.runner.kind === "context-index") return runContextIndexRunner(cwd, clioEntry, task.timeoutMs, target, env);
|
|
226
365
|
if (task.runner.kind === "context-init")
|
|
227
366
|
return runContextInitRunner(task.runner, cwd, clioEntry, task.timeoutMs, target, env);
|
|
228
|
-
return runClioRunRunner(task.runner, cwd, clioEntry, task.timeoutMs, target, env);
|
|
367
|
+
return runClioRunRunner(task.runner, cwd, clioEntry, task.timeoutMs, target, env, task.metrics.readObservation);
|
|
229
368
|
}
|
|
230
369
|
|
|
231
370
|
/**
|
|
232
|
-
* Run the task-outcome
|
|
233
|
-
* recorded
|
|
234
|
-
*
|
|
235
|
-
*
|
|
371
|
+
* Run the task-outcome grader and report what it found. A nonzero exit is
|
|
372
|
+
* recorded rather than thrown so the verdict can report a failed outcome with
|
|
373
|
+
* healthy machinery. A task that declares no measure commands reports nothing
|
|
374
|
+
* and leaves its final pass decision to the runner and gating verifiers.
|
|
236
375
|
*/
|
|
237
376
|
async function measureTaskOutcome(
|
|
238
377
|
task: LoadedEvalSuiteV2["suite"]["tasks"][number],
|
|
239
378
|
cwd: string,
|
|
240
|
-
|
|
379
|
+
env?: NodeJS.ProcessEnv,
|
|
380
|
+
): Promise<{ metrics: Record<string, number | boolean>; executionObservation?: EvalExecutionObservationV1 }> {
|
|
241
381
|
const commands = task.verify.measure ?? [];
|
|
242
|
-
if (commands.length === 0) return {};
|
|
243
|
-
const result = await runCommandVerifiers(commands, cwd, task.timeoutMs);
|
|
244
|
-
|
|
382
|
+
if (commands.length === 0) return { metrics: {} };
|
|
383
|
+
const result = await runCommandVerifiers(commands, cwd, task.timeoutMs, env);
|
|
384
|
+
const behavioral = graderBehaviorMeasurement(result.stdout);
|
|
385
|
+
return {
|
|
386
|
+
metrics: {
|
|
387
|
+
"task.exitCode": result.exitCode,
|
|
388
|
+
"task.solved": result.exitCode === 0,
|
|
389
|
+
...behavioral.metrics,
|
|
390
|
+
},
|
|
391
|
+
...(behavioral.executionObservation === undefined ? {} : { executionObservation: behavioral.executionObservation }),
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function graderBehaviorMeasurement(stdout: string): {
|
|
396
|
+
metrics: Record<string, number | boolean>;
|
|
397
|
+
executionObservation?: EvalExecutionObservationV1;
|
|
398
|
+
} {
|
|
399
|
+
const metrics: Record<string, number | boolean> = {};
|
|
400
|
+
let executionObservation: EvalExecutionObservationV1 | undefined;
|
|
401
|
+
for (const line of stdout.split(/\r?\n/u)) {
|
|
402
|
+
if (line.trim().length === 0) continue;
|
|
403
|
+
let value: unknown;
|
|
404
|
+
try {
|
|
405
|
+
value = JSON.parse(line) as unknown;
|
|
406
|
+
} catch {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
if (!isRecord(value)) continue;
|
|
410
|
+
if (value.schema === "clio.eval.measure.v1" && isRecord(value.metrics)) {
|
|
411
|
+
for (const [key, metric] of Object.entries(value.metrics)) {
|
|
412
|
+
if (key !== "claims.unsupported" && key !== "completion.reported") continue;
|
|
413
|
+
if (typeof metric === "boolean" || (typeof metric === "number" && Number.isFinite(metric))) metrics[key] = metric;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (value.schema === "clio.eval.execution-observation.v1") {
|
|
417
|
+
executionObservation = parseExecutionObservation(value);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return { metrics, ...(executionObservation === undefined ? {} : { executionObservation }) };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function parseExecutionObservation(value: Record<string, unknown>): EvalExecutionObservationV1 {
|
|
424
|
+
const policies = isRecord(value.policyHashes) ? value.policyHashes : {};
|
|
425
|
+
const project = isRecord(value.projectContext) ? value.projectContext : null;
|
|
426
|
+
return {
|
|
427
|
+
compositionHash: nullableDigest(value.compositionHash),
|
|
428
|
+
target: nullableString(value.target),
|
|
429
|
+
wireModel: nullableString(value.wireModel),
|
|
430
|
+
runtime: nullableString(value.runtime),
|
|
431
|
+
thinkingLevel: nullableString(value.thinkingLevel),
|
|
432
|
+
toolSignature: nullableDigest(value.toolSignature),
|
|
433
|
+
autonomy: nullableString(value.autonomy),
|
|
434
|
+
policyHashes: { rulePack: nullableDigest(policies.rulePack), project: nullableDigest(policies.project) },
|
|
435
|
+
projectContext:
|
|
436
|
+
project === null
|
|
437
|
+
? null
|
|
438
|
+
: {
|
|
439
|
+
tier: nullableString(project.tier),
|
|
440
|
+
contentHash: nullableDigest(project.contentHash),
|
|
441
|
+
chars: nullableNonNegativeInteger(project.chars),
|
|
442
|
+
sections: stringArray(project.sections),
|
|
443
|
+
rulesApplied: stringArray(project.rulesApplied),
|
|
444
|
+
operatorProfileApplied:
|
|
445
|
+
typeof project.operatorProfileApplied === "boolean" ? project.operatorProfileApplied : null,
|
|
446
|
+
},
|
|
447
|
+
};
|
|
245
448
|
}
|
|
246
449
|
|
|
247
450
|
async function runVerifiers(
|
|
@@ -293,6 +496,7 @@ function buildArtifact(
|
|
|
293
496
|
evalId: string,
|
|
294
497
|
results: EvalArtifactResultV4[],
|
|
295
498
|
clioEntry: string,
|
|
499
|
+
servingConfiguration: EvalServingConfigurationV1,
|
|
296
500
|
): EvalArtifactV4 {
|
|
297
501
|
const passed = results.filter((result) => result.pass).length;
|
|
298
502
|
return {
|
|
@@ -301,7 +505,11 @@ function buildArtifact(
|
|
|
301
505
|
suite: { id: loaded.suite.suite.id, hash: loaded.hash },
|
|
302
506
|
clio: evalClioProvenance({ entry: clioEntry }),
|
|
303
507
|
environment: evalEnvironmentProvenance(),
|
|
304
|
-
matrix:
|
|
508
|
+
matrix: {
|
|
509
|
+
...artifactMatrixIdentity(loaded.suite.matrix.targets),
|
|
510
|
+
...(loaded.suite.matrix.dimensions === undefined ? {} : { dimensions: loaded.suite.matrix.dimensions }),
|
|
511
|
+
},
|
|
512
|
+
servingConfiguration,
|
|
305
513
|
summary: {
|
|
306
514
|
runs: results.length,
|
|
307
515
|
passed,
|
|
@@ -310,6 +518,9 @@ function buildArtifact(
|
|
|
310
518
|
tokens: tokenAccountingFrom(results),
|
|
311
519
|
wallTimeMs: results.reduce((sum, result) => sum + wallTimeMetric(result.metrics), 0),
|
|
312
520
|
},
|
|
521
|
+
aggregates: aggregateEvalVerdicts(
|
|
522
|
+
results.flatMap((result) => (result.verdict === undefined ? [] : [result.verdict])),
|
|
523
|
+
),
|
|
313
524
|
results,
|
|
314
525
|
};
|
|
315
526
|
}
|
|
@@ -317,3 +528,23 @@ function buildArtifact(
|
|
|
317
528
|
function assertionMessage(assertion: EvalMetricAssertion, actual: number | string | boolean | null): string {
|
|
318
529
|
return `assertion failed: ${assertion.metric} ${assertion.op} ${String(assertion.value)} (actual ${JSON.stringify(actual)})`;
|
|
319
530
|
}
|
|
531
|
+
|
|
532
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
533
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function nullableString(value: unknown): string | null {
|
|
537
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function nullableDigest(value: unknown): string | null {
|
|
541
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/u.test(value) ? value : null;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function nullableNonNegativeInteger(value: unknown): number | null {
|
|
545
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : null;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function stringArray(value: unknown): string[] {
|
|
549
|
+
return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === "string") : [];
|
|
550
|
+
}
|
|
@@ -12,12 +12,13 @@ export async function runCommandVerifiers(
|
|
|
12
12
|
commands: ReadonlyArray<string>,
|
|
13
13
|
cwd: string,
|
|
14
14
|
timeoutMs: number,
|
|
15
|
+
env?: NodeJS.ProcessEnv,
|
|
15
16
|
): Promise<CommandVerifierResult> {
|
|
16
17
|
let stdout = "";
|
|
17
18
|
let stderr = "";
|
|
18
19
|
let wallTimeMs = 0;
|
|
19
20
|
for (const command of commands) {
|
|
20
|
-
const result = await runShellCommand(command, cwd, timeoutMs);
|
|
21
|
+
const result = await runShellCommand(command, cwd, timeoutMs, env);
|
|
21
22
|
stdout += result.stdout;
|
|
22
23
|
stderr += result.stderr;
|
|
23
24
|
wallTimeMs += result.wallTimeMs;
|
|
@@ -1,29 +1,161 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { cp, lstat, mkdtemp, rm } from "node:fs/promises";
|
|
2
3
|
import { tmpdir } from "node:os";
|
|
3
4
|
import { relative, resolve } from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
4
6
|
import type { EvalWorkspaceV2 } from "../schema/suite.js";
|
|
5
7
|
import type { PreparedEvalWorkspace } from "./local.js";
|
|
6
8
|
|
|
9
|
+
const execFileAsync = promisify(execFile);
|
|
10
|
+
const GIT_FILE_LIST_LIMIT_BYTES = 128 * 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
export interface EvalWorkspaceCopyOptions {
|
|
13
|
+
recursive: true;
|
|
14
|
+
filter: (source: string, destination: string) => boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type EvalWorkspaceCopy = (
|
|
18
|
+
source: string,
|
|
19
|
+
destination: string,
|
|
20
|
+
options: EvalWorkspaceCopyOptions,
|
|
21
|
+
) => Promise<void>;
|
|
22
|
+
|
|
23
|
+
export interface PrepareTempCopyWorkspaceOptions {
|
|
24
|
+
/** Test/integration seam; production uses node:fs/promises.cp. */
|
|
25
|
+
copy?: EvalWorkspaceCopy;
|
|
26
|
+
/** Parent for both workspace and suite-owned state directories. */
|
|
27
|
+
tempRoot?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
7
30
|
export async function prepareTempCopyWorkspace(
|
|
8
31
|
baseDir: string,
|
|
9
32
|
workspace: EvalWorkspaceV2,
|
|
33
|
+
options: PrepareTempCopyWorkspaceOptions = {},
|
|
10
34
|
): Promise<PreparedEvalWorkspace> {
|
|
11
35
|
const source = resolve(baseDir, workspace.path ?? ".");
|
|
12
|
-
const dest = await mkdtemp(resolve(tmpdir(), "clio-eval-workspace-"));
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
const dest = await mkdtemp(resolve(options.tempRoot ?? tmpdir(), "clio-eval-workspace-"));
|
|
37
|
+
try {
|
|
38
|
+
const selection = await gitCopySelection(source);
|
|
39
|
+
const excludes = workspace.excludes ?? [];
|
|
40
|
+
const copyWorkspace = options.copy ?? defaultCopy;
|
|
41
|
+
await copyWorkspace(source, dest, {
|
|
42
|
+
recursive: true,
|
|
43
|
+
filter: (path) => shouldCopy(relative(source, path), excludes, selection),
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
dir: dest,
|
|
47
|
+
cleanup: async () => {
|
|
48
|
+
await rm(dest, { recursive: true, force: true });
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
// `mkdtemp` succeeded before either Git enumeration or the recursive
|
|
53
|
+
// copy could throw. The preparer still owns that incomplete destination.
|
|
54
|
+
await rm(dest, { recursive: true, force: true });
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
24
57
|
}
|
|
25
58
|
|
|
26
59
|
function isExcluded(rel: string, excludes: ReadonlyArray<string>): boolean {
|
|
27
60
|
const normalized = rel.replaceAll("\\", "/");
|
|
28
61
|
return excludes.some((entry) => normalized === entry || normalized.startsWith(`${entry.replaceAll("\\", "/")}/`));
|
|
29
62
|
}
|
|
63
|
+
|
|
64
|
+
async function defaultCopy(source: string, destination: string, options: EvalWorkspaceCopyOptions): Promise<void> {
|
|
65
|
+
await cp(source, destination, options);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface GitCopySelection {
|
|
69
|
+
files: ReadonlySet<string>;
|
|
70
|
+
directories: ReadonlySet<string>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function shouldCopy(
|
|
74
|
+
relativePath: string,
|
|
75
|
+
excludes: ReadonlyArray<string>,
|
|
76
|
+
selection: GitCopySelection | null,
|
|
77
|
+
): boolean {
|
|
78
|
+
const normalized = relativePath.replaceAll("\\", "/");
|
|
79
|
+
if (normalized.length === 0) return true;
|
|
80
|
+
if (isExcluded(normalized, excludes)) return false;
|
|
81
|
+
if (selection === null) return true;
|
|
82
|
+
return selection.files.has(normalized) || selection.directories.has(normalized);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A Git checkout supplies the authoritative copy set: tracked files plus
|
|
87
|
+
* untracked files that are not ignored. Outside Git, null preserves the
|
|
88
|
+
* existing recursive-copy behavior.
|
|
89
|
+
*/
|
|
90
|
+
async function gitCopySelection(source: string): Promise<GitCopySelection | null> {
|
|
91
|
+
let inside: string;
|
|
92
|
+
try {
|
|
93
|
+
inside = await gitOutput(source, ["rev-parse", "--is-inside-work-tree"]);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Outside a checkout, Git's refusal selects the legacy recursive path. A
|
|
96
|
+
// checkout whose Git query broke must fail closed: falling back there can
|
|
97
|
+
// silently copy the ignored data this branch exists to exclude.
|
|
98
|
+
if (await hasGitMarker(source)) throw error;
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (inside.trim() !== "true") return null;
|
|
102
|
+
|
|
103
|
+
const output = await gitOutput(source, [
|
|
104
|
+
"--literal-pathspecs",
|
|
105
|
+
"ls-files",
|
|
106
|
+
"-z",
|
|
107
|
+
"--cached",
|
|
108
|
+
"--others",
|
|
109
|
+
"--exclude-standard",
|
|
110
|
+
"--",
|
|
111
|
+
".",
|
|
112
|
+
]);
|
|
113
|
+
const files = new Set<string>();
|
|
114
|
+
const directories = new Set<string>();
|
|
115
|
+
for (const path of output.split("\0")) {
|
|
116
|
+
if (path.length === 0) continue;
|
|
117
|
+
const normalized = normalizeGitPath(path);
|
|
118
|
+
if (normalized === null) throw new Error("git ls-files returned a path outside the eval workspace");
|
|
119
|
+
files.add(normalized);
|
|
120
|
+
let separator = normalized.lastIndexOf("/");
|
|
121
|
+
while (separator >= 0) {
|
|
122
|
+
directories.add(normalized.slice(0, separator));
|
|
123
|
+
separator = normalized.lastIndexOf("/", separator - 1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { files, directories };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function gitOutput(cwd: string, args: ReadonlyArray<string>): Promise<string> {
|
|
130
|
+
const { stdout } = await execFileAsync("git", [...args], {
|
|
131
|
+
cwd,
|
|
132
|
+
encoding: "utf8",
|
|
133
|
+
maxBuffer: GIT_FILE_LIST_LIMIT_BYTES,
|
|
134
|
+
});
|
|
135
|
+
return stdout;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function normalizeGitPath(path: string): string | null {
|
|
139
|
+
const normalized = path.replaceAll("\\", "/").replace(/^\.\//u, "");
|
|
140
|
+
if (normalized.length === 0 || normalized.startsWith("/") || /^[A-Za-z]:\//u.test(normalized)) return null;
|
|
141
|
+
const segments = normalized.split("/");
|
|
142
|
+
if (segments.some((segment) => segment.length === 0 || segment === "." || segment === "..")) return null;
|
|
143
|
+
return normalized;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function hasGitMarker(source: string): Promise<boolean> {
|
|
147
|
+
let current = resolve(source);
|
|
148
|
+
while (true) {
|
|
149
|
+
try {
|
|
150
|
+
await lstat(resolve(current, ".git"));
|
|
151
|
+
return true;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
const code =
|
|
154
|
+
typeof error === "object" && error !== null && "code" in error ? (error as { code?: unknown }).code : undefined;
|
|
155
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
156
|
+
}
|
|
157
|
+
const parent = resolve(current, "..");
|
|
158
|
+
if (parent === current) return false;
|
|
159
|
+
current = parent;
|
|
160
|
+
}
|
|
161
|
+
}
|