@iowarp/clio-coder 0.3.0 → 0.3.1
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 +33 -0
- package/README.md +50 -53
- package/dist/acp-FPR54DGL.js +138 -0
- package/dist/{agents-Q4MYPMUW.js → agents-OGPIHPJH.js} +33 -25
- package/dist/assets/codewiki.json +1 -0
- package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/LICENSE +21 -0
- package/dist/assets/grammars/notices/@vscode__tree-sitter-wasm/cgmanifest.json +16 -0
- package/dist/assets/grammars/notices/tree-sitter-wasms/LICENSE +24 -0
- package/dist/assets/grammars/tree-sitter-c-sharp.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-c.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-cpp.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-go.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-java.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-python.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/assets/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/assets/grammars/tree-sitter.wasm +0 -0
- package/dist/{auth-O6HYIJ6J.js → auth-IC3K6NIZ.js} +22 -15
- package/dist/{chunk-KKKPTZLM.js → chunk-2STGEDFV.js} +8 -1
- package/dist/chunk-3I7MS7N2.js +40 -0
- package/dist/chunk-3R73A4XB.js +69 -0
- package/dist/{chunk-26BZQOAD.js → chunk-474KN5II.js} +86 -115
- package/dist/{chunk-NI4DEJMC.js → chunk-4QKXUHSR.js} +26 -3
- package/dist/{chunk-X3BR7HWV.js → chunk-5WIGXA4T.js} +7 -1
- package/dist/{chunk-TFSSEXL6.js → chunk-673JJUWJ.js} +14 -4
- package/dist/{chunk-GNGDQYDU.js → chunk-7SS2CTV2.js} +27922 -1249
- package/dist/{chunk-7RBKDI66.js → chunk-ASND7OZK.js} +104 -12
- package/dist/{chunk-X3NE4WVW.js → chunk-BNAZZHFG.js} +7 -1
- package/dist/{chunk-RQDWMVRB.js → chunk-BSU2YIWB.js} +9 -3
- package/dist/{chunk-CLJ5HLUD.js → chunk-C4PTHK7P.js} +24 -5
- package/dist/{chunk-QV47YRF4.js → chunk-CGKSTWHD.js} +7 -1
- package/dist/{chunk-BYEU76JP.js → chunk-DKGKUHFA.js} +34 -9
- package/dist/chunk-EYOKLTMF.js +170 -0
- package/dist/{chunk-PS4PFJQP.js → chunk-FCIH3BIZ.js} +15258 -14130
- package/dist/{chunk-2J63S4SF.js → chunk-FST4FYJB.js} +82 -14
- package/dist/{chunk-3DANZDGR.js → chunk-G34LV2PF.js} +34 -9
- package/dist/{chunk-BBUH4VAA.js → chunk-GAEBEQVI.js} +34 -21
- package/dist/chunk-GB6QRBXN.js +8543 -0
- package/dist/chunk-GEP36Y4X.js +12796 -0
- package/dist/{chunk-TKHQ4DGZ.js → chunk-H6F6BYOH.js} +496 -121
- package/dist/{chunk-UGYAX5YI.js → chunk-HQQID6OA.js} +10 -5
- package/dist/chunk-IHXBNWMM.js +137 -0
- package/dist/{chunk-ME6DNWIU.js → chunk-IS3ONKU3.js} +7 -1
- package/dist/{chunk-DXQNI4PC.js → chunk-IWHMRKLL.js} +8 -1
- package/dist/chunk-K2ITRMHZ.js +642 -0
- package/dist/{chunk-KBR4MZZR.js → chunk-K5XEMXTI.js} +7 -1
- package/dist/{chunk-AMFR5YA3.js → chunk-KKNLWXI6.js} +24 -10
- package/dist/{chunk-JWHCJDCI.js → chunk-KY56HMHH.js} +145 -61
- package/dist/{chunk-E3NYWENM.js → chunk-LU4TK2PR.js} +52 -7
- package/dist/{chunk-UWIJNAOB.js → chunk-M5T5VO65.js} +109 -42
- package/dist/{chunk-JPOWPFCU.js → chunk-MAR7Y6HW.js} +38 -16
- package/dist/chunk-N6F52NLF.js +6951 -0
- package/dist/{chunk-UHTSULZS.js → chunk-NMOX6HFD.js} +20 -7
- package/dist/{chunk-WD6AJM35.js → chunk-OLBBMFRD.js} +30 -13
- package/dist/{chunk-VOO7NYPP.js → chunk-PFEFKVGL.js} +22 -6
- package/dist/{chunk-6CWDTGUC.js → chunk-PIUMUEMV.js} +7 -1
- package/dist/chunk-PV4JUBVJ.js +462 -0
- package/dist/{chunk-527KG6XR.js → chunk-Q3RUPKEJ.js} +22 -4
- package/dist/{chunk-D5YD55AR.js → chunk-Q5WJOSJ7.js} +11 -1
- package/dist/{chunk-5N2FG33Q.js → chunk-R346GLFC.js} +7 -1
- package/dist/{chunk-4UQA7NCT.js → chunk-R5KLMSBV.js} +7 -2
- package/dist/{chunk-PIDUD6M2.js → chunk-RDLVBZEO.js} +11 -6
- package/dist/{chunk-O4EJEDHO.js → chunk-RPTR2H26.js} +40 -31
- package/dist/{chunk-HBU5MTAM.js → chunk-SVNXH4L2.js} +7 -1
- package/dist/{chunk-XNISANGE.js → chunk-TEKV33Q5.js} +122 -57
- package/dist/chunk-TXOTCRLG.js +15 -0
- package/dist/{chunk-VPAWTYLY.js → chunk-WEPFGWHJ.js} +7 -1
- package/dist/{chunk-HMYNFFY4.js → chunk-XPLRXC72.js} +7 -2
- package/dist/{chunk-UU3R62TT.js → chunk-XYWBQRDM.js} +13 -4
- package/dist/{chunk-7BHLZB3A.js → chunk-ZPY3JZ5E.js} +32 -10
- package/dist/cli/index.js +39 -33
- package/dist/clio-BZVGEUFJ.js +58 -0
- package/dist/{components-JVHMUBEB.js → components-DMAOEKFB.js} +26 -9
- package/dist/{config-ZFCDBMDC.js → config-IRUQ7SE4.js} +42 -31
- package/dist/configure-S7S6F6CL.js +32 -0
- package/dist/{context-CDXTP2MP.js → context-3KWFLHJG.js} +17 -11
- package/dist/{context-E3KIFVXI.js → context-5RADCKTR.js} +33 -28
- package/dist/{context-clear-3F4PLXOS.js → context-clear-7TSNPAAI.js} +36 -28
- package/dist/{context-index-Q7YSYTR3.js → context-index-W4RLWOQH.js} +10 -4
- package/dist/{docs-YIETIWZI.js → docs-5AWSPS37.js} +16 -10
- package/dist/doctor-UC5NAJYQ.js +77 -0
- package/dist/{eval-B3KZZESM.js → eval-U6TJHRLX.js} +77 -24
- package/dist/{evidence-V67CHM35.js → evidence-YEGUW4L3.js} +34 -26
- package/dist/{evolve-YDZSUQYA.js → evolve-TXARCTPG.js} +39 -26
- package/dist/{extensions-SRG7XCAH.js → extensions-OZFJ3A3G.js} +13 -7
- package/dist/{fleet-CA2CRTVG.js → fleet-6G3DHNYE.js} +45 -37
- package/dist/fleet-preflight-DSNT37JK.js +23 -0
- package/dist/{init-2OZDJE2D.js → init-KZ5QTF6M.js} +42 -34
- package/dist/{memory-3PIQQAKX.js → memory-73ESV5YC.js} +35 -27
- package/dist/{models-DY35XI7Y.js → models-A4PVNWJK.js} +34 -23
- package/dist/{paths-5OMXW7Z4.js → paths-C4H6IV77.js} +12 -6
- package/dist/preload-6WVMHX3A.js +13 -0
- package/dist/{reset-PIFYNOS3.js → reset-BGW6OGMV.js} +17 -11
- package/dist/{run-3VSPP24F.js → run-YTPEYQOH.js} +60 -48
- package/dist/{share-D36RQCXM.js → share-YIFFV4NQ.js} +16 -10
- package/dist/{skills-F2MRLELY.js → skills-2V6RA3OQ.js} +18 -11
- package/dist/{skills-eval-E2ZTW4PL.js → skills-eval-S2TVJO4F.js} +45 -36
- package/dist/{targets-DZMEZAH4.js → targets-TYXLPB23.js} +61 -59
- package/dist/{trace-7NYCUI2J.js → trace-GGOJ6Q6Z.js} +9 -3
- package/dist/{uninstall-AD3JWHBB.js → uninstall-LLLT4F4W.js} +14 -8
- package/dist/{upgrade-WYYBKGDY.js → upgrade-33G2LMM5.js} +45 -23
- package/dist/{usage-ULIDAGFF.js → usage-ZAFSXKKG.js} +35 -27
- package/dist/version-KGEG7EPO.js +21 -0
- package/dist/{wiki-generate-PKFIX6OB.js → wiki-generate-NUQCVOQ3.js} +55 -45
- package/dist/worker/entry.js +101 -43
- package/docs/README.md +12 -9
- package/docs/acp.md +28 -5
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +23 -2
- package/docs/artifact-placement.md +101 -0
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +4 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +125 -41
- package/docs/configuration-and-targets.md +88 -18
- package/docs/context-engine.md +6 -2
- package/docs/development-pipeline.md +77 -0
- package/docs/documentation-coverage.md +4 -4
- package/docs/documentation-guide.md +3 -3
- package/docs/environment-variables.md +2 -1
- package/docs/eval-runner.md +3 -3
- package/docs/evals-internal.md +2 -2
- package/docs/evidence-and-memory.md +6 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +18 -11
- package/docs/fleet-demo-runbook.md +22 -23
- package/docs/fleet-dispatch.md +111 -12
- package/docs/glossary.md +107 -15
- package/docs/html/agents_blueprint.html +3 -3
- package/docs/html/alcf_blueprint.html +3 -3
- package/docs/html/architecture_blueprint.html +3 -3
- package/docs/html/commands_blueprint.html +187 -42
- package/docs/html/configuration_blueprint.html +7 -7
- package/docs/html/context_blueprint.html +3 -3
- package/docs/html/documentation_blueprint.html +1 -1
- package/docs/html/environment_blueprint.html +3 -3
- package/docs/html/eval_blueprint.html +4 -4
- package/docs/html/evals_internal_blueprint.html +3 -3
- package/docs/html/evolution_blueprint.html +3 -3
- package/docs/html/extensions_blueprint.html +3 -3
- package/docs/html/fleet_dispatch_blueprint.html +8 -8
- package/docs/html/index.html +5 -5
- package/docs/html/lifecycle_blueprint.html +4 -4
- package/docs/html/memory_blueprint.html +3 -3
- package/docs/html/middleware_blueprint.html +3 -3
- package/docs/html/models_blueprint.html +2 -2
- package/docs/html/observability_blueprint.html +2 -2
- package/docs/html/provider_adapter_blueprint.html +3 -3
- package/docs/html/safety_blueprint.html +3 -3
- package/docs/html/skills_blueprint.html +2 -2
- package/docs/html/soak_blueprint.html +3 -3
- package/docs/html/tool_usage_blueprint.html +4 -4
- package/docs/html/tools_blueprint.html +2 -2
- package/docs/html/trace_blueprint.html +3 -3
- package/docs/html/tui_design_blueprint.html +100 -40
- package/docs/html/validation_blueprint.html +1 -1
- package/docs/html/worker_dispatch_blueprint.html +3 -3
- package/docs/installation-and-lifecycle.md +62 -11
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +2 -2
- package/docs/observability.md +3 -3
- package/docs/proactive-memory.md +12 -12
- package/docs/prompt-envelope-and-tools.md +2 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +4 -0
- package/docs/safety-model.md +21 -5
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +2 -2
- package/docs/skills-marketplace.md +1 -1
- package/docs/time-conventions.md +101 -0
- package/docs/tool-usage.md +5 -4
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +83 -27
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +16 -16
- package/skills/README.md +5 -6
- package/skills/git/file-ticket/SKILL.md +107 -0
- package/skills/git/file-ticket/evals.md +54 -0
- package/skills/git/fix-issue/SKILL.md +106 -0
- package/skills/git/fix-issue/evals.md +71 -0
- package/skills/git/ship/SKILL.md +92 -0
- package/skills/git/ship/evals.md +72 -0
- package/skills/skill-marketplace.json +12 -20
- package/src/cli/acp.ts +88 -0
- package/src/cli/agents.ts +60 -0
- package/src/cli/args.ts +280 -0
- package/src/cli/argv.ts +170 -0
- package/src/cli/ask.ts +34 -0
- package/src/cli/auth.ts +468 -0
- package/src/cli/bootstrap-generate.ts +498 -0
- package/src/cli/clio.ts +34 -0
- package/src/cli/components.ts +195 -0
- package/src/cli/config-inspect.ts +330 -0
- package/src/cli/config.ts +72 -0
- package/src/cli/configure-interop.ts +67 -0
- package/src/cli/configure-layout.ts +172 -0
- package/src/cli/configure.ts +1487 -0
- package/src/cli/context-clear.ts +76 -0
- package/src/cli/context-index.ts +102 -0
- package/src/cli/context.ts +316 -0
- package/src/cli/default-target.ts +63 -0
- package/src/cli/docs.ts +340 -0
- package/src/cli/doctor.ts +54 -0
- package/src/cli/eval.ts +315 -0
- package/src/cli/evidence.ts +222 -0
- package/src/cli/evolve.ts +219 -0
- package/src/cli/extensions.ts +205 -0
- package/src/cli/fleet.ts +827 -0
- package/src/cli/index.ts +309 -0
- package/src/cli/init.ts +219 -0
- package/src/cli/initial-message.ts +47 -0
- package/src/cli/internal-dispatch.ts +55 -0
- package/src/cli/memory.ts +180 -0
- package/src/cli/models.ts +257 -0
- package/src/cli/modes/json-stream.ts +85 -0
- package/src/cli/modes/jsonl.ts +9 -0
- package/src/cli/modes/print.ts +589 -0
- package/src/cli/oauth-manual-input.ts +72 -0
- package/src/cli/oauth-select.ts +36 -0
- package/src/cli/output-guard.ts +48 -0
- package/src/cli/paths.ts +44 -0
- package/src/cli/provider-target.ts +113 -0
- package/src/cli/removal.ts +72 -0
- package/src/cli/reset.ts +253 -0
- package/src/cli/run.ts +497 -0
- package/src/cli/share.ts +243 -0
- package/src/cli/shared.ts +60 -0
- package/src/cli/skills-eval.ts +1225 -0
- package/src/cli/skills.ts +470 -0
- package/src/cli/steer-channel.ts +86 -0
- package/src/cli/targets.ts +1150 -0
- package/src/cli/text-layout.ts +102 -0
- package/src/cli/trace.ts +274 -0
- package/src/cli/uninstall.ts +405 -0
- package/src/cli/upgrade.ts +270 -0
- package/src/cli/usage.ts +869 -0
- package/src/cli/validate-model.ts +28 -0
- package/src/cli/version.ts +7 -0
- package/src/cli/wiki-generate.ts +509 -0
- package/src/core/artifact-paths.ts +43 -0
- package/src/core/bash-exec.ts +242 -0
- package/src/core/boot-trace.ts +37 -0
- package/src/core/bus-events.ts +728 -0
- package/src/core/bus-trace.ts +53 -0
- package/src/core/c-header-language.ts +265 -0
- package/src/core/clio-repo.ts +78 -0
- package/src/core/config.ts +1593 -0
- package/src/core/context-floor.ts +33 -0
- package/src/core/defaults.ts +625 -0
- package/src/core/domain-loader.ts +170 -0
- package/src/core/event-bus.ts +94 -0
- package/src/core/file-references.ts +282 -0
- package/src/core/guardrails.ts +185 -0
- package/src/core/headless-permission.ts +21 -0
- package/src/core/init.ts +223 -0
- package/src/core/json-payload.ts +58 -0
- package/src/core/package-root.ts +54 -0
- package/src/core/path-canonical.ts +37 -0
- package/src/core/process-identity.ts +41 -0
- package/src/core/recent-models.ts +91 -0
- package/src/core/release.ts +2 -0
- package/src/core/residency-protection.ts +56 -0
- package/src/core/resolve-config-value.ts +88 -0
- package/src/core/response-schema.ts +279 -0
- package/src/core/run-overrides.ts +77 -0
- package/src/core/safe-exec.ts +193 -0
- package/src/core/safe-id.ts +37 -0
- package/src/core/safe-resource-write.ts +162 -0
- package/src/core/session-routing.ts +339 -0
- package/src/core/settings-layers.ts +220 -0
- package/src/core/shared-bus.ts +8 -0
- package/src/core/shell-quote.ts +26 -0
- package/src/core/skill-activation.ts +190 -0
- package/src/core/startup-timer.ts +37 -0
- package/src/core/state-file-lock.ts +207 -0
- package/src/core/termination.ts +202 -0
- package/src/core/timers.ts +38 -0
- package/src/core/tool-names.ts +65 -0
- package/src/core/verification-scripts.ts +13 -0
- package/src/core/workspace-files.ts +492 -0
- package/src/core/xdg.ts +177 -0
- package/src/domains/agents/builtins/architect.md +4 -4
- package/src/domains/agents/builtins/coder.md +3 -2
- package/src/domains/agents/builtins/debugger.md +1 -1
- package/src/domains/agents/builtins/documenter.md +1 -1
- package/src/domains/agents/builtins/git-master.md +3 -3
- package/src/domains/agents/builtins/provenance.md +1 -1
- package/src/domains/agents/builtins/researcher.md +1 -1
- package/src/domains/agents/builtins/scout.md +1 -1
- package/src/domains/agents/builtins/tester.md +1 -1
- package/src/domains/agents/builtins/verifier.md +1 -1
- package/src/domains/agents/builtins/wiki-writer.md +1 -1
- package/src/domains/agents/catalog.ts +172 -0
- package/src/domains/agents/contract.ts +16 -0
- package/src/domains/agents/extension.ts +110 -0
- package/src/domains/agents/fleet-commands.ts +154 -0
- package/src/domains/agents/fleet-contract.ts +797 -0
- package/src/domains/agents/frontmatter.ts +40 -0
- package/src/domains/agents/index.ts +102 -0
- package/src/domains/agents/manifest.ts +6 -0
- package/src/domains/agents/recipe-schema.ts +202 -0
- package/src/domains/agents/recipe.ts +125 -0
- package/src/domains/agents/registry.ts +111 -0
- package/src/domains/agents/result-contract.ts +1265 -0
- package/src/domains/agents/spec.ts +371 -0
- package/src/domains/agents/write-boundary.ts +109 -0
- package/src/domains/components/diff.ts +99 -0
- package/src/domains/components/hash.ts +21 -0
- package/src/domains/components/index.ts +3 -0
- package/src/domains/components/scan.ts +304 -0
- package/src/domains/components/snapshot.ts +108 -0
- package/src/domains/components/types.ts +82 -0
- package/src/domains/config/agent-namespace.ts +13 -0
- package/src/domains/config/classify.ts +94 -0
- package/src/domains/config/contract.ts +22 -0
- package/src/domains/config/extension.ts +136 -0
- package/src/domains/config/index.ts +13 -0
- package/src/domains/config/keybindings.ts +164 -0
- package/src/domains/config/manifest.ts +6 -0
- package/src/domains/config/watcher.ts +41 -0
- package/src/domains/context/adoption.ts +880 -0
- package/src/domains/context/bootstrap-prompt.ts +273 -0
- package/src/domains/context/bootstrap.ts +1409 -0
- package/src/domains/context/clear.ts +86 -0
- package/src/domains/context/clio-md.ts +327 -0
- package/src/domains/context/codewiki/cmake.ts +356 -0
- package/src/domains/context/codewiki/cooperative.ts +46 -0
- package/src/domains/context/codewiki/digest.ts +135 -0
- package/src/domains/context/codewiki/grammar-assets.ts +33 -0
- package/src/domains/context/codewiki/indexer.ts +1614 -0
- package/src/domains/context/codewiki/tree-sitter.ts +771 -0
- package/src/domains/context/contract.ts +62 -0
- package/src/domains/context/excluded-dirs.ts +1 -0
- package/src/domains/context/extension.ts +407 -0
- package/src/domains/context/fingerprint.ts +196 -0
- package/src/domains/context/index.ts +164 -0
- package/src/domains/context/init-options.ts +69 -0
- package/src/domains/context/manifest.ts +6 -0
- package/src/domains/context/operator-profile.ts +157 -0
- package/src/domains/context/project-metadata.ts +510 -0
- package/src/domains/context/project-rules.ts +227 -0
- package/src/domains/context/prompt-context.ts +62 -0
- package/src/domains/context/refresh.ts +170 -0
- package/src/domains/context/sibling-files.ts +10 -0
- package/src/domains/context/state.ts +237 -0
- package/src/domains/context/wiki/assemble.ts +303 -0
- package/src/domains/context/wiki/frontmatter.ts +210 -0
- package/src/domains/context/wiki/generate.ts +590 -0
- package/src/domains/context/wiki/layout.ts +111 -0
- package/src/domains/context/wiki/meta.ts +221 -0
- package/src/domains/context/wiki/plan-store.ts +217 -0
- package/src/domains/context/wiki/plan.ts +250 -0
- package/src/domains/context/wiki/prompts.ts +270 -0
- package/src/domains/context/wiki/staleness.ts +198 -0
- package/src/domains/dispatch/active-route-planner.ts +168 -0
- package/src/domains/dispatch/admission-queue.ts +150 -0
- package/src/domains/dispatch/admission.ts +334 -0
- package/src/domains/dispatch/agent-candidates.ts +307 -0
- package/src/domains/dispatch/agent-ledger-hub.ts +71 -0
- package/src/domains/dispatch/agent-ledger-store.ts +257 -0
- package/src/domains/dispatch/agent-ledger.ts +212 -0
- package/src/domains/dispatch/agent-plan-adapter.ts +85 -0
- package/src/domains/dispatch/assignment-events.ts +132 -0
- package/src/domains/dispatch/assignment-reconcile.ts +76 -0
- package/src/domains/dispatch/assignment-store.ts +162 -0
- package/src/domains/dispatch/assignment.ts +278 -0
- package/src/domains/dispatch/backoff.ts +59 -0
- package/src/domains/dispatch/batch-store.ts +134 -0
- package/src/domains/dispatch/batch-tracker.ts +66 -0
- package/src/domains/dispatch/capability-match.ts +155 -0
- package/src/domains/dispatch/capacity-lease.ts +413 -0
- package/src/domains/dispatch/code-step-store.ts +52 -0
- package/src/domains/dispatch/code-step.ts +368 -0
- package/src/domains/dispatch/contract.ts +278 -0
- package/src/domains/dispatch/dedup.ts +133 -0
- package/src/domains/dispatch/event-pump.ts +251 -0
- package/src/domains/dispatch/execution-handoff.ts +29 -0
- package/src/domains/dispatch/execution-plan.ts +328 -0
- package/src/domains/dispatch/execution-role.ts +277 -0
- package/src/domains/dispatch/execution-scheduler.ts +583 -0
- package/src/domains/dispatch/extension.ts +5869 -0
- package/src/domains/dispatch/failure-classification.ts +141 -0
- package/src/domains/dispatch/fleet-plan.ts +225 -0
- package/src/domains/dispatch/fleet-preflight.ts +394 -0
- package/src/domains/dispatch/gate-decisions.ts +1040 -0
- package/src/domains/dispatch/gate-role-prompts.ts +58 -0
- package/src/domains/dispatch/heartbeat.ts +27 -0
- package/src/domains/dispatch/index.ts +137 -0
- package/src/domains/dispatch/joint-route-adapter.ts +114 -0
- package/src/domains/dispatch/joint-route-resolver.ts +283 -0
- package/src/domains/dispatch/manifest.ts +6 -0
- package/src/domains/dispatch/orphan-recovery.ts +224 -0
- package/src/domains/dispatch/outcome.ts +109 -0
- package/src/domains/dispatch/phase-timing.ts +47 -0
- package/src/domains/dispatch/placement.ts +202 -0
- package/src/domains/dispatch/receipt-findings.ts +255 -0
- package/src/domains/dispatch/receipt-integrity.ts +378 -0
- package/src/domains/dispatch/recovery-candidates.ts +74 -0
- package/src/domains/dispatch/reproducibility.ts +48 -0
- package/src/domains/dispatch/reservation-store.ts +501 -0
- package/src/domains/dispatch/route-approval.ts +177 -0
- package/src/domains/dispatch/route-candidates.ts +57 -0
- package/src/domains/dispatch/route-decision.ts +1079 -0
- package/src/domains/dispatch/route-facts.ts +187 -0
- package/src/domains/dispatch/route-history.ts +238 -0
- package/src/domains/dispatch/route-observer.ts +488 -0
- package/src/domains/dispatch/route-policy.ts +327 -0
- package/src/domains/dispatch/route-quality.ts +256 -0
- package/src/domains/dispatch/route-readiness.ts +120 -0
- package/src/domains/dispatch/routing-intent.ts +287 -0
- package/src/domains/dispatch/run-identity.ts +56 -0
- package/src/domains/dispatch/scout-transition.ts +130 -0
- package/src/domains/dispatch/state.ts +277 -0
- package/src/domains/dispatch/tool-stats.ts +132 -0
- package/src/domains/dispatch/transport.ts +209 -0
- package/src/domains/dispatch/types.ts +785 -0
- package/src/domains/dispatch/validation-grounding.ts +154 -0
- package/src/domains/dispatch/validation.ts +721 -0
- package/src/domains/dispatch/worker-protocol.ts +275 -0
- package/src/domains/dispatch/worker-spawn.ts +566 -0
- package/src/domains/dispatch/write-boundary-enforcer.ts +97 -0
- package/src/domains/dispatch/write-boundary.ts +426 -0
- package/src/domains/eval/artifacts/redact.ts +42 -0
- package/src/domains/eval/artifacts/store.ts +173 -0
- package/src/domains/eval/compare/compare.ts +33 -0
- package/src/domains/eval/compare/gates.ts +66 -0
- package/src/domains/eval/compare/thresholds.ts +92 -0
- package/src/domains/eval/harness-metrics.ts +62 -0
- package/src/domains/eval/index.ts +66 -0
- package/src/domains/eval/metrics/chaos-stream.ts +93 -0
- package/src/domains/eval/metrics/context.ts +34 -0
- package/src/domains/eval/metrics/coverage.ts +21 -0
- package/src/domains/eval/metrics/evidence.ts +182 -0
- package/src/domains/eval/metrics/fleet-loop-stream.ts +196 -0
- package/src/domains/eval/metrics/invariants.ts +736 -0
- package/src/domains/eval/metrics/latency.ts +4 -0
- package/src/domains/eval/metrics/token-stream.ts +139 -0
- package/src/domains/eval/metrics/tokens.ts +51 -0
- package/src/domains/eval/metrics/tool-calls.ts +7 -0
- package/src/domains/eval/proactive-memory.ts +279 -0
- package/src/domains/eval/provenance.ts +35 -0
- package/src/domains/eval/report.ts +234 -0
- package/src/domains/eval/reports/json.ts +5 -0
- package/src/domains/eval/reports/junit.ts +23 -0
- package/src/domains/eval/reports/markdown.ts +20 -0
- package/src/domains/eval/reports/swe-jsonl.ts +16 -0
- package/src/domains/eval/reports/text.ts +25 -0
- package/src/domains/eval/run-compare.ts +274 -0
- package/src/domains/eval/runner.ts +244 -0
- package/src/domains/eval/runners/clio-run.ts +148 -0
- package/src/domains/eval/runners/context-index.ts +56 -0
- package/src/domains/eval/runners/context-init.ts +203 -0
- package/src/domains/eval/runners/external-command.ts +350 -0
- package/src/domains/eval/runtime-paths.ts +171 -0
- package/src/domains/eval/schema/artifact.ts +75 -0
- package/src/domains/eval/schema/suite.ts +235 -0
- package/src/domains/eval/schema/validate.ts +339 -0
- package/src/domains/eval/store.ts +392 -0
- package/src/domains/eval/suites/load.ts +74 -0
- package/src/domains/eval/suites/matrix.ts +19 -0
- package/src/domains/eval/suites/resolve.ts +44 -0
- package/src/domains/eval/suites/run.ts +325 -0
- package/src/domains/eval/task-file.ts +186 -0
- package/src/domains/eval/types.ts +141 -0
- package/src/domains/eval/verifiers/command.ts +27 -0
- package/src/domains/eval/verifiers/file-exists.ts +6 -0
- package/src/domains/eval/verifiers/patch.ts +22 -0
- package/src/domains/eval/workspaces/git.ts +41 -0
- package/src/domains/eval/workspaces/local.ts +17 -0
- package/src/domains/eval/workspaces/temp-copy.ts +29 -0
- package/src/domains/evidence/build.ts +1714 -0
- package/src/domains/evidence/eval.ts +605 -0
- package/src/domains/evidence/failure-attribution.ts +54 -0
- package/src/domains/evidence/finish-contract-map.ts +42 -0
- package/src/domains/evidence/index.ts +55 -0
- package/src/domains/evidence/provenance.ts +200 -0
- package/src/domains/evidence/redact.ts +81 -0
- package/src/domains/evidence/store.ts +291 -0
- package/src/domains/evidence/types.ts +325 -0
- package/src/domains/evolution/SELF_EDIT_GATE.md +91 -0
- package/src/domains/evolution/index.ts +26 -0
- package/src/domains/evolution/manifest.ts +154 -0
- package/src/domains/evolution/validate.ts +304 -0
- package/src/domains/extensions/contract.ts +22 -0
- package/src/domains/extensions/discovery.ts +193 -0
- package/src/domains/extensions/extension.ts +46 -0
- package/src/domains/extensions/index.ts +34 -0
- package/src/domains/extensions/manager.ts +30 -0
- package/src/domains/extensions/manifest.ts +6 -0
- package/src/domains/extensions/resources.ts +38 -0
- package/src/domains/extensions/state.ts +224 -0
- package/src/domains/extensions/types.ts +83 -0
- package/src/domains/interop/consent.ts +185 -0
- package/src/domains/interop/contract.ts +29 -0
- package/src/domains/interop/detect.ts +173 -0
- package/src/domains/interop/extension.ts +59 -0
- package/src/domains/interop/index.ts +34 -0
- package/src/domains/interop/manifest.ts +6 -0
- package/src/domains/interop/registry.ts +139 -0
- package/src/domains/interop/state.ts +92 -0
- package/src/domains/interop/types.ts +109 -0
- package/src/domains/lifecycle/contract.ts +11 -0
- package/src/domains/lifecycle/doctor.ts +536 -0
- package/src/domains/lifecycle/extension.ts +23 -0
- package/src/domains/lifecycle/index.ts +21 -0
- package/src/domains/lifecycle/install-method.ts +51 -0
- package/src/domains/lifecycle/manifest.ts +6 -0
- package/src/domains/lifecycle/migrations/index.ts +94 -0
- package/src/domains/lifecycle/state.ts +93 -0
- package/src/domains/lifecycle/upgrade-notice.ts +35 -0
- package/src/domains/lifecycle/version.ts +35 -0
- package/src/domains/memory/index.ts +128 -0
- package/src/domains/memory/operations.ts +143 -0
- package/src/domains/memory/prompt-section.ts +86 -0
- package/src/domains/memory/proposal.ts +180 -0
- package/src/domains/memory/store.ts +163 -0
- package/src/domains/memory/task-bank.ts +229 -0
- package/src/domains/memory/task-memory-handoff.ts +222 -0
- package/src/domains/memory/task-memory-policy.ts +602 -0
- package/src/domains/memory/task-memory-status.ts +58 -0
- package/src/domains/memory/task-memory-telemetry.ts +346 -0
- package/src/domains/memory/task-memory-trace.ts +89 -0
- package/src/domains/memory/types.ts +74 -0
- package/src/domains/memory/validate.ts +297 -0
- package/src/domains/middleware/budget.ts +216 -0
- package/src/domains/middleware/contract.ts +35 -0
- package/src/domains/middleware/dispatch-nudge.ts +469 -0
- package/src/domains/middleware/extension.ts +129 -0
- package/src/domains/middleware/hook-receipts.ts +70 -0
- package/src/domains/middleware/hooks-io.ts +174 -0
- package/src/domains/middleware/hooks.ts +432 -0
- package/src/domains/middleware/index.ts +154 -0
- package/src/domains/middleware/manifest.ts +6 -0
- package/src/domains/middleware/memory-intervention.ts +701 -0
- package/src/domains/middleware/rules.ts +42 -0
- package/src/domains/middleware/runtime.ts +333 -0
- package/src/domains/middleware/skills-reminder.ts +187 -0
- package/src/domains/middleware/snapshot.ts +71 -0
- package/src/domains/middleware/stalled-turn.ts +71 -0
- package/src/domains/middleware/task-board-reminder.ts +87 -0
- package/src/domains/middleware/task-nudge.ts +77 -0
- package/src/domains/middleware/tool-choice-control.ts +42 -0
- package/src/domains/middleware/types.ts +131 -0
- package/src/domains/middleware/validate.ts +329 -0
- package/src/domains/observability/accountability.ts +67 -0
- package/src/domains/observability/contract.ts +168 -0
- package/src/domains/observability/cost.ts +206 -0
- package/src/domains/observability/evidence-index.ts +123 -0
- package/src/domains/observability/extension.ts +281 -0
- package/src/domains/observability/index.ts +78 -0
- package/src/domains/observability/manifest.ts +10 -0
- package/src/domains/observability/metrics.ts +37 -0
- package/src/domains/observability/projection.ts +477 -0
- package/src/domains/observability/telemetry.ts +89 -0
- package/src/domains/observability/trace-store.ts +1454 -0
- package/src/domains/prompts/compiler.ts +556 -0
- package/src/domains/prompts/contract.ts +30 -0
- package/src/domains/prompts/extension.ts +304 -0
- package/src/domains/prompts/fragment-loader.ts +139 -0
- package/src/domains/prompts/fragments/identity/clio.md +3 -2
- package/src/domains/prompts/fragments/identity/self-awareness.md +63 -0
- package/src/domains/prompts/fragments/operating/contract.md +10 -1
- package/src/domains/prompts/hash.ts +73 -0
- package/src/domains/prompts/index.ts +32 -0
- package/src/domains/prompts/manifest.ts +8 -0
- package/src/domains/prompts/memory-intervention.ts +57 -0
- package/src/domains/prompts/preload.ts +82 -0
- package/src/domains/providers/auth/api-key.ts +47 -0
- package/src/domains/providers/auth/backend-file.ts +69 -0
- package/src/domains/providers/auth/backend-memory.ts +17 -0
- package/src/domains/providers/auth/index.ts +37 -0
- package/src/domains/providers/auth/oauth.ts +36 -0
- package/src/domains/providers/auth/storage.ts +667 -0
- package/src/domains/providers/capabilities.ts +50 -0
- package/src/domains/providers/catalog.ts +162 -0
- package/src/domains/providers/contract.ts +151 -0
- package/src/domains/providers/credentials.ts +20 -0
- package/src/domains/providers/eligibility.ts +24 -0
- package/src/domains/providers/errors.ts +64 -0
- package/src/domains/providers/extension.ts +536 -0
- package/src/domains/providers/index.ts +142 -0
- package/src/domains/providers/knowledge-base-path.ts +102 -0
- package/src/domains/providers/manifest.ts +6 -0
- package/src/domains/providers/model-capabilities.ts +136 -0
- package/src/domains/providers/model-discovery.ts +144 -0
- package/src/domains/providers/model-family.ts +32 -0
- package/src/domains/providers/model-runtime-capabilities.ts +712 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +2 -2
- package/src/domains/providers/plugins.ts +59 -0
- package/src/domains/providers/probe/fingerprint.ts +44 -0
- package/src/domains/providers/probe/http.ts +114 -0
- package/src/domains/providers/probe/reasoning.ts +111 -0
- package/src/domains/providers/registry.ts +166 -0
- package/src/domains/providers/resolver.ts +156 -0
- package/src/domains/providers/runtime-resolution.ts +720 -0
- package/src/domains/providers/runtimes/antigravity/antigravity-code.ts +90 -0
- package/src/domains/providers/runtimes/builtins.ts +78 -0
- package/src/domains/providers/runtimes/claude/claude-code.ts +38 -0
- package/src/domains/providers/runtimes/claude/claude-sdk.ts +38 -0
- package/src/domains/providers/runtimes/claude/common.ts +55 -0
- package/src/domains/providers/runtimes/cloud/alcf.ts +171 -0
- package/src/domains/providers/runtimes/cloud/anthropic-max.ts +71 -0
- package/src/domains/providers/runtimes/cloud/anthropic.ts +43 -0
- package/src/domains/providers/runtimes/cloud/bedrock.ts +35 -0
- package/src/domains/providers/runtimes/cloud/deepseek.ts +50 -0
- package/src/domains/providers/runtimes/cloud/google.ts +41 -0
- package/src/domains/providers/runtimes/cloud/groq.ts +46 -0
- package/src/domains/providers/runtimes/cloud/mistral.ts +46 -0
- package/src/domains/providers/runtimes/cloud/openai-codex.ts +49 -0
- package/src/domains/providers/runtimes/cloud/openai.ts +46 -0
- package/src/domains/providers/runtimes/cloud/openrouter.ts +111 -0
- package/src/domains/providers/runtimes/common/lmstudio-logger.ts +32 -0
- package/src/domains/providers/runtimes/common/local-synth.ts +165 -0
- package/src/domains/providers/runtimes/common/probe-helpers.ts +474 -0
- package/src/domains/providers/runtimes/local-native/lemonade-anthropic.ts +27 -0
- package/src/domains/providers/runtimes/local-native/lemonade-openai.ts +26 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +88 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +207 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +180 -0
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +126 -0
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +98 -0
- package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +491 -0
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +117 -0
- package/src/domains/providers/runtimes/local-native/sglang.ts +26 -0
- package/src/domains/providers/runtimes/local-native/vllm.ts +27 -0
- package/src/domains/providers/runtimes/protocol/anthropic-compat.ts +104 -0
- package/src/domains/providers/runtimes/protocol/anthropic-messages.ts +28 -0
- package/src/domains/providers/runtimes/protocol/bedrock.ts +26 -0
- package/src/domains/providers/runtimes/protocol/google.ts +27 -0
- package/src/domains/providers/runtimes/protocol/openai-compat.ts +145 -0
- package/src/domains/providers/support.ts +238 -0
- package/src/domains/providers/thinking-control-policy.ts +85 -0
- package/src/domains/providers/types/capability-flags.ts +114 -0
- package/src/domains/providers/types/cost-provenance.ts +7 -0
- package/src/domains/providers/types/inference.ts +41 -0
- package/src/domains/providers/types/knowledge-base.ts +156 -0
- package/src/domains/providers/types/local-model-quirks.ts +218 -0
- package/src/domains/providers/types/runtime-descriptor.ts +170 -0
- package/src/domains/providers/types/target-descriptor.ts +38 -0
- package/src/domains/resources/collision.ts +92 -0
- package/src/domains/resources/common-loader.ts +140 -0
- package/src/domains/resources/contract.ts +25 -0
- package/src/domains/resources/extension.ts +61 -0
- package/src/domains/resources/index.ts +74 -0
- package/src/domains/resources/loader.ts +86 -0
- package/src/domains/resources/manifest.ts +6 -0
- package/src/domains/resources/prompts/loader.ts +232 -0
- package/src/domains/resources/prompts/substitute.ts +65 -0
- package/src/domains/resources/skills/content-hash.ts +105 -0
- package/src/domains/resources/skills/evals.ts +175 -0
- package/src/domains/resources/skills/install.ts +410 -0
- package/src/domains/resources/skills/loader.ts +978 -0
- package/src/domains/resources/skills/marketplace.ts +255 -0
- package/src/domains/resources/skills/provenance-pin.ts +115 -0
- package/src/domains/safety/action-classifier.ts +268 -0
- package/src/domains/safety/approval-axis.ts +23 -0
- package/src/domains/safety/audit.ts +487 -0
- package/src/domains/safety/autonomy.ts +174 -0
- package/src/domains/safety/call-target.ts +59 -0
- package/src/domains/safety/contract.ts +65 -0
- package/src/domains/safety/damage-control.ts +44 -0
- package/src/domains/safety/default-path-policy.ts +117 -0
- package/src/domains/safety/extension.ts +383 -0
- package/src/domains/safety/finish-contract-registration.ts +128 -0
- package/src/domains/safety/finish-contract.ts +554 -0
- package/src/domains/safety/index.ts +44 -0
- package/src/domains/safety/loop-detector.ts +143 -0
- package/src/domains/safety/manifest.ts +6 -0
- package/src/domains/safety/path-policy.ts +164 -0
- package/src/domains/safety/policy-engine.ts +994 -0
- package/src/domains/safety/project-policy.ts +391 -0
- package/src/domains/safety/protected-artifacts-registration.ts +189 -0
- package/src/domains/safety/protected-artifacts.ts +993 -0
- package/src/domains/safety/rejection-feedback.ts +102 -0
- package/src/domains/safety/rigor.ts +72 -0
- package/src/domains/safety/rule-compiler.ts +48 -0
- package/src/domains/safety/rule-pack-loader.ts +92 -0
- package/src/domains/safety/run-effects.ts +197 -0
- package/src/domains/safety/scope.ts +61 -0
- package/src/domains/scheduling/budget.ts +35 -0
- package/src/domains/scheduling/cluster.ts +175 -0
- package/src/domains/scheduling/contract.ts +30 -0
- package/src/domains/scheduling/extension.ts +91 -0
- package/src/domains/scheduling/index.ts +12 -0
- package/src/domains/scheduling/manifest.ts +6 -0
- package/src/domains/session/archive-readers.ts +208 -0
- package/src/domains/session/checkpoint.ts +38 -0
- package/src/domains/session/compaction/auto.ts +89 -0
- package/src/domains/session/compaction/branch-summary.ts +186 -0
- package/src/domains/session/compaction/compact.ts +493 -0
- package/src/domains/session/compaction/cut-point.ts +171 -0
- package/src/domains/session/compaction/defaults.ts +34 -0
- package/src/domains/session/compaction/mask-observations.ts +265 -0
- package/src/domains/session/compaction/session-entries.ts +18 -0
- package/src/domains/session/compaction/tokens.ts +147 -0
- package/src/domains/session/context-accounting.ts +583 -0
- package/src/domains/session/context-ledger.ts +348 -0
- package/src/domains/session/contract.ts +144 -0
- package/src/domains/session/cwd-fallback.ts +65 -0
- package/src/domains/session/entries.ts +509 -0
- package/src/domains/session/extension.ts +322 -0
- package/src/domains/session/history.ts +172 -0
- package/src/domains/session/index.ts +84 -0
- package/src/domains/session/manager.ts +128 -0
- package/src/domains/session/manifest.ts +6 -0
- package/src/domains/session/migrations/index.ts +27 -0
- package/src/domains/session/prompt-manifest.ts +173 -0
- package/src/domains/session/protected-artifact-journal.ts +241 -0
- package/src/domains/session/protected-artifacts.ts +90 -0
- package/src/domains/session/retry.ts +238 -0
- package/src/domains/session/task-board.ts +375 -0
- package/src/domains/session/tree/active-path.ts +98 -0
- package/src/domains/session/tree/fork.ts +160 -0
- package/src/domains/session/tree/manager.ts +128 -0
- package/src/domains/session/tree/navigator.ts +177 -0
- package/src/domains/session/tree/preview.ts +259 -0
- package/src/domains/session/usage.ts +148 -0
- package/src/domains/session/workspace/git-probe.ts +161 -0
- package/src/domains/session/workspace/index.ts +4 -0
- package/src/domains/session/workspace/project-type.ts +217 -0
- package/src/domains/session/workspace/snapshot.ts +74 -0
- package/src/domains/share/archive.ts +684 -0
- package/src/domains/share/contract.ts +9 -0
- package/src/domains/share/extension.ts +26 -0
- package/src/domains/share/index.ts +28 -0
- package/src/domains/share/manifest.ts +6 -0
- package/src/engine/acp/adapter.ts +397 -0
- package/src/engine/acp/errors.ts +32 -0
- package/src/engine/acp/event-mapper.ts +154 -0
- package/src/engine/acp/server.ts +656 -0
- package/src/engine/acp/tool-mediator.ts +686 -0
- package/src/engine/acp/transport.ts +654 -0
- package/src/engine/acp/types.ts +170 -0
- package/src/engine/agent.ts +29 -0
- package/src/engine/ai.ts +202 -0
- package/src/engine/alcf-oauth.ts +198 -0
- package/src/engine/antigravity/subprocess-runtime.ts +205 -0
- package/src/engine/apis/degraded-inference.ts +95 -0
- package/src/engine/apis/index.ts +20 -0
- package/src/engine/apis/llamacpp-residency.ts +252 -0
- package/src/engine/apis/lmstudio-native.ts +1438 -0
- package/src/engine/apis/lmstudio-residency.ts +92 -0
- package/src/engine/apis/ollama-native.ts +507 -0
- package/src/engine/apis/openai-completions.ts +716 -0
- package/src/engine/apis/output-budget.ts +99 -0
- package/src/engine/apis/residency-lock.ts +29 -0
- package/src/engine/apis/residency.ts +640 -0
- package/src/engine/apis/resident-models.ts +42 -0
- package/src/engine/apis/sampling-overrides.ts +38 -0
- package/src/engine/apis/thinking-replay.ts +11 -0
- package/src/engine/claude/sdk-runtime.ts +685 -0
- package/src/engine/claude/session-id.ts +5 -0
- package/src/engine/claude/subprocess-runtime.ts +316 -0
- package/src/engine/claude/tool-safety.ts +400 -0
- package/src/engine/external-subprocess.ts +23 -0
- package/src/engine/harmony-response.ts +165 -0
- package/src/engine/loop-guard.ts +1127 -0
- package/src/engine/oauth.ts +192 -0
- package/src/engine/pi-mono-names.ts +13 -0
- package/src/engine/provider-payload.ts +315 -0
- package/src/engine/select-list.ts +53 -0
- package/src/engine/session.ts +834 -0
- package/src/engine/strip-tokenizer-sentinels.ts +125 -0
- package/src/engine/tui.ts +103 -0
- package/src/engine/types.ts +66 -0
- package/src/engine/worker-events.ts +100 -0
- package/src/engine/worker-runtime.ts +1022 -0
- package/src/engine/worker-tools.ts +203 -0
- package/src/entry/orchestrator.ts +1873 -0
- package/src/interactive/application-controller.ts +346 -0
- package/src/interactive/bus-notices.ts +265 -0
- package/src/interactive/chat-loop-messages.ts +626 -0
- package/src/interactive/chat-loop-policy.ts +18 -0
- package/src/interactive/chat-loop.ts +865 -0
- package/src/interactive/chat-panel.ts +1694 -0
- package/src/interactive/chat-renderer.ts +1038 -0
- package/src/interactive/clio-editor.ts +166 -0
- package/src/interactive/command-fallbacks.ts +23 -0
- package/src/interactive/command-output.ts +85 -0
- package/src/interactive/context-activity.ts +203 -0
- package/src/interactive/context-meter.ts +223 -0
- package/src/interactive/context-overlay.ts +220 -0
- package/src/interactive/cost-overlay.ts +279 -0
- package/src/interactive/dispatch-board.ts +1319 -0
- package/src/interactive/dispatch-steering.ts +86 -0
- package/src/interactive/editor-bash.ts +46 -0
- package/src/interactive/editor-steer.ts +51 -0
- package/src/interactive/editor-submit.ts +303 -0
- package/src/interactive/external-editor.ts +67 -0
- package/src/interactive/follow-up-queue-panel.ts +75 -0
- package/src/interactive/footer/dashboard.ts +534 -0
- package/src/interactive/footer/notifications.ts +275 -0
- package/src/interactive/footer/widgets.ts +1246 -0
- package/src/interactive/footer-panel.ts +187 -0
- package/src/interactive/format-time.ts +85 -0
- package/src/interactive/index.ts +11 -0
- package/src/interactive/interactive-application.ts +755 -0
- package/src/interactive/interactive-event-projection.ts +380 -0
- package/src/interactive/interactive-input-runtime.ts +261 -0
- package/src/interactive/interactive-presentation.ts +452 -0
- package/src/interactive/interactive-shell.ts +134 -0
- package/src/interactive/interactive-slash-runtime.ts +492 -0
- package/src/interactive/interactive-subscriptions.ts +111 -0
- package/src/interactive/interactive-tickers.ts +152 -0
- package/src/interactive/keybinding-manager.ts +496 -0
- package/src/interactive/layout.ts +20 -0
- package/src/interactive/leader-key.ts +143 -0
- package/src/interactive/loop-guard-interrupt.ts +101 -0
- package/src/interactive/memory-overlay.ts +280 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +110 -0
- package/src/interactive/overlay-auth-lifecycle.ts +308 -0
- package/src/interactive/overlay-frame.ts +502 -0
- package/src/interactive/overlay-general-openers.ts +217 -0
- package/src/interactive/overlay-key-routing.ts +213 -0
- package/src/interactive/overlay-lifecycle.ts +413 -0
- package/src/interactive/overlay-model-selectors.ts +127 -0
- package/src/interactive/overlay-permission-lifecycle.ts +344 -0
- package/src/interactive/overlay-resource-openers.ts +112 -0
- package/src/interactive/overlay-session-lifecycle.ts +270 -0
- package/src/interactive/overlay-transitions.ts +57 -0
- package/src/interactive/overlays/agents.ts +69 -0
- package/src/interactive/overlays/ask-user.ts +921 -0
- package/src/interactive/overlays/auth-dialog.ts +221 -0
- package/src/interactive/overlays/context-reset.ts +108 -0
- package/src/interactive/overlays/cwd-fallback.ts +141 -0
- package/src/interactive/overlays/extensions.ts +54 -0
- package/src/interactive/overlays/help-reference.ts +140 -0
- package/src/interactive/overlays/interop.ts +139 -0
- package/src/interactive/overlays/keybinding-detail.ts +90 -0
- package/src/interactive/overlays/list-overlay.ts +606 -0
- package/src/interactive/overlays/message-picker.ts +155 -0
- package/src/interactive/overlays/model-selector.ts +1091 -0
- package/src/interactive/overlays/prompts.ts +72 -0
- package/src/interactive/overlays/session-selector-search.ts +31 -0
- package/src/interactive/overlays/session-selector.ts +266 -0
- package/src/interactive/overlays/settings.ts +3363 -0
- package/src/interactive/overlays/skills-hub.ts +237 -0
- package/src/interactive/overlays/tree-selector.ts +387 -0
- package/src/interactive/permission-overlay.ts +170 -0
- package/src/interactive/render-trace.ts +125 -0
- package/src/interactive/renderers/branch-summary.ts +86 -0
- package/src/interactive/renderers/code-ink.ts +420 -0
- package/src/interactive/renderers/compaction-summary.ts +112 -0
- package/src/interactive/renderers/diff.ts +126 -0
- package/src/interactive/renderers/notice.ts +28 -0
- package/src/interactive/renderers/retry-status.ts +31 -0
- package/src/interactive/renderers/structured.ts +89 -0
- package/src/interactive/renderers/tool-execution.ts +1104 -0
- package/src/interactive/renderers/worker-entry.ts +264 -0
- package/src/interactive/session-last-turn.ts +186 -0
- package/src/interactive/session-transcript.ts +105 -0
- package/src/interactive/session-usage-reseed.ts +74 -0
- package/src/interactive/slash-autocomplete.ts +364 -0
- package/src/interactive/slash-commands.ts +1319 -0
- package/src/interactive/slash-spec.ts +453 -0
- package/src/interactive/status/controller.ts +256 -0
- package/src/interactive/status/index.ts +23 -0
- package/src/interactive/status/state-machine.ts +530 -0
- package/src/interactive/status/summary.ts +170 -0
- package/src/interactive/status/types.ts +121 -0
- package/src/interactive/status/verbs.ts +163 -0
- package/src/interactive/status/watchdog.ts +24 -0
- package/src/interactive/tasks-overlay.ts +186 -0
- package/src/interactive/theme/components.ts +55 -0
- package/src/interactive/theme/glyphs.ts +70 -0
- package/src/interactive/theme/index.ts +15 -0
- package/src/interactive/theme/labels.ts +88 -0
- package/src/interactive/theme/rules.ts +138 -0
- package/src/interactive/theme/segments.ts +88 -0
- package/src/interactive/theme/tokens.ts +121 -0
- package/src/interactive/thinking-level-policy.ts +6 -0
- package/src/interactive/tool-prose-loop.ts +122 -0
- package/src/interactive/tool-prose-registration.ts +48 -0
- package/src/interactive/turn-context.ts +854 -0
- package/src/interactive/turn-middleware.ts +283 -0
- package/src/interactive/turn-persistence.ts +441 -0
- package/src/interactive/turn-queues.ts +169 -0
- package/src/interactive/turn-recovery.ts +306 -0
- package/src/interactive/turn-runtime.ts +802 -0
- package/src/interactive/turn-state.ts +123 -0
- package/src/interactive/view/artifacts.ts +1342 -0
- package/src/interactive/view/view-overlay.ts +732 -0
- package/src/interactive/welcome-dashboard.ts +536 -0
- package/src/interactive/worker-receipts.ts +94 -0
- package/src/interactive/worker-replay.ts +111 -0
- package/src/interactive/worker-share.ts +100 -0
- package/src/interactive/worker-stream.ts +442 -0
- package/src/interactive/workspace-facts.ts +141 -0
- package/src/tools/agent-tools.ts +382 -0
- package/src/tools/artifact.ts +103 -0
- package/src/tools/ask-user.ts +656 -0
- package/src/tools/bash.ts +176 -0
- package/src/tools/bootstrap.ts +474 -0
- package/src/tools/codewiki/code-nav.ts +563 -0
- package/src/tools/codewiki/shared.ts +115 -0
- package/src/tools/compete-worktrees.ts +898 -0
- package/src/tools/context/docs-engine.ts +619 -0
- package/src/tools/context/index.ts +435 -0
- package/src/tools/credential-present.ts +151 -0
- package/src/tools/dispatch-arguments.ts +228 -0
- package/src/tools/dispatch-plan.ts +741 -0
- package/src/tools/dispatch-scout.ts +461 -0
- package/src/tools/dispatch.ts +3338 -0
- package/src/tools/edit-diff.ts +534 -0
- package/src/tools/edit.ts +128 -0
- package/src/tools/executables.ts +22 -0
- package/src/tools/file-mutation-queue.ts +35 -0
- package/src/tools/find.ts +292 -0
- package/src/tools/grep.ts +495 -0
- package/src/tools/ignore-policy.ts +169 -0
- package/src/tools/ledger.ts +206 -0
- package/src/tools/ls.ts +110 -0
- package/src/tools/monitor.ts +797 -0
- package/src/tools/network-policy.ts +29 -0
- package/src/tools/observation.ts +421 -0
- package/src/tools/observers.ts +99 -0
- package/src/tools/path-utils.ts +79 -0
- package/src/tools/policy.ts +154 -0
- package/src/tools/profiles.ts +108 -0
- package/src/tools/read.ts +173 -0
- package/src/tools/registry.ts +1157 -0
- package/src/tools/result-shaping.ts +204 -0
- package/src/tools/safe-exec.ts +128 -0
- package/src/tools/spawn-hygiene.ts +182 -0
- package/src/tools/steer.ts +88 -0
- package/src/tools/string-enum.ts +15 -0
- package/src/tools/tasks.ts +168 -0
- package/src/tools/truncate-utf8.ts +17 -0
- package/src/tools/truncate.ts +202 -0
- package/src/tools/verify/frontend.ts +685 -0
- package/src/tools/verify/index.ts +66 -0
- package/src/tools/verify/scripts.ts +108 -0
- package/src/tools/web-fetch.ts +777 -0
- package/src/tools/worker-evidence.ts +215 -0
- package/src/tools/write.ts +52 -0
- package/src/utils/exif-orientation.ts +192 -0
- package/src/utils/git.ts +24 -0
- package/src/utils/image-resize.ts +148 -0
- package/src/utils/photon.ts +103 -0
- package/src/worker/control-lane.ts +22 -0
- package/src/worker/entry.ts +257 -0
- package/src/worker/event-projection.ts +56 -0
- package/src/worker/heartbeat.ts +24 -0
- package/src/worker/ledger-mirror.ts +95 -0
- package/src/worker/ndjson.ts +40 -0
- package/src/worker/protocol.ts +529 -0
- package/src/worker/resource-facts.ts +79 -0
- package/src/worker/runtime-registry.ts +12 -0
- package/src/worker/spec-contract.ts +654 -0
- package/src/worker/stdin-demux.ts +316 -0
- package/dist/acp-UMLFVA3F.js +0 -92
- package/dist/chunk-262G75JS.js +0 -35
- package/dist/chunk-5LDRNKX2.js +0 -1063
- package/dist/chunk-67MTHP2E.js +0 -135
- package/dist/chunk-GOTUR54M.js +0 -9
- package/dist/chunk-TPOCL34A.js +0 -2876
- package/dist/chunk-XV4ZJ6ZM.js +0 -3177
- package/dist/clio-KIQ5SNDS.js +0 -53
- package/dist/configure-G4E3A2PG.js +0 -27
- package/dist/doctor-M5HJJZOL.js +0 -61
- package/dist/domains/agents/builtins/architect.md +0 -33
- package/dist/domains/agents/builtins/coder.md +0 -31
- package/dist/domains/agents/builtins/context-bootstrap.md +0 -38
- package/dist/domains/agents/builtins/debugger.md +0 -30
- package/dist/domains/agents/builtins/documenter.md +0 -31
- package/dist/domains/agents/builtins/git-master.md +0 -30
- package/dist/domains/agents/builtins/provenance.md +0 -30
- package/dist/domains/agents/builtins/researcher.md +0 -71
- package/dist/domains/agents/builtins/scout.md +0 -42
- package/dist/domains/agents/builtins/tester.md +0 -31
- package/dist/domains/agents/builtins/verifier.md +0 -30
- package/dist/domains/agents/builtins/wiki-writer.md +0 -41
- package/dist/fleet-preflight-CLIAX7YR.js +0 -21
- package/dist/preload-KZVHET2B.js +0 -11
- package/dist/version-ROZ6CZKH.js +0 -16
- package/skills/git/commit-crafting/SKILL.md +0 -79
- package/skills/git/commit-crafting/evals.md +0 -92
- package/skills/git/create-pr/SKILL.md +0 -116
- package/skills/git/create-pr/evals.md +0 -114
- package/skills/git/investigate-issue/SKILL.md +0 -139
- package/skills/git/investigate-issue/evals.md +0 -94
- package/skills/git/review-changes/SKILL.md +0 -103
- package/skills/git/review-changes/evals.md +0 -85
|
@@ -0,0 +1,1225 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { cp, mkdir, mkdtemp, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join, resolve } from "node:path";
|
|
7
|
+
import { performance } from "node:perf_hooks";
|
|
8
|
+
import { combineBashOutput, runBashCommand } from "../core/bash-exec.js";
|
|
9
|
+
import { HEADLESS_PERMISSION_DENIED_MARKER } from "../core/headless-permission.js";
|
|
10
|
+
import { clioDataDir, clioStateDir } from "../core/xdg.js";
|
|
11
|
+
import {
|
|
12
|
+
type EvalHarnessMetrics,
|
|
13
|
+
type EvalRunArtifact,
|
|
14
|
+
type EvalRunRecord,
|
|
15
|
+
evalClioProvenance,
|
|
16
|
+
evalEnvironmentProvenance,
|
|
17
|
+
summarizeEvalResults,
|
|
18
|
+
writeEvalArtifact,
|
|
19
|
+
ZERO_EVAL_HARNESS_METRICS,
|
|
20
|
+
} from "../domains/eval/index.js";
|
|
21
|
+
import { buildEvalEvidence } from "../domains/evidence/index.js";
|
|
22
|
+
import {
|
|
23
|
+
discoverMarketplaceSkills,
|
|
24
|
+
loadSkills,
|
|
25
|
+
parseSkillEvals,
|
|
26
|
+
type SkillEvalScenario,
|
|
27
|
+
} from "../domains/resources/index.js";
|
|
28
|
+
import { NO_NETWORK_TOOLS_ENV } from "../tools/network-policy.js";
|
|
29
|
+
import { formatColumns, printError } from "./shared.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Executable skill evals (experimental). Every catalog skill ships an
|
|
33
|
+
* `evals.md` with RED-GREEN scenarios; this lane executes them instead of
|
|
34
|
+
* trusting the prose: per scenario one baseline headless run (no skills), one
|
|
35
|
+
* treatment run (the skill loaded via the explicit --skill path and invoked
|
|
36
|
+
* with /skill:<name>), then one judge run that scores each Expected bullet
|
|
37
|
+
* pass/fail from the two transcripts.
|
|
38
|
+
*
|
|
39
|
+
* Deltas from the task-file eval harness, recorded rather than papered over:
|
|
40
|
+
* the evals domain verifies with setup/verifier commands and has no judge, so
|
|
41
|
+
* bullet verdicts here come from a model run, not command exit codes. The
|
|
42
|
+
* synthesized EvalRunArtifact carries scenario-level records with empty
|
|
43
|
+
* command lists, receipt-backed token/cost totals when headless main-agent
|
|
44
|
+
* receipts are present, and per-bullet detail in a `skill-eval.json` sidecar
|
|
45
|
+
* registered in the bundle's `overview.json` files list.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
const DEFAULT_RUN_TIMEOUT_MS = 600_000;
|
|
49
|
+
/**
|
|
50
|
+
* The autonomy the acting arms run at.
|
|
51
|
+
*
|
|
52
|
+
* A headless `clio-coder run` has no operator to answer a permission ask, so below
|
|
53
|
+
* full-auto every exec-class call in an arm dies with the harness's own denial
|
|
54
|
+
* and the judge scores a skill that was never allowed to act. Operator decision
|
|
55
|
+
* of 2026-08-13 (docs/findings/HARNESS-NOTES.md item 4): eval arms run
|
|
56
|
+
* full-auto, because their workspaces are per-run copies created under a
|
|
57
|
+
* private temp root and removed when the scenario ends. The judge arm is
|
|
58
|
+
* excluded: it scores text and is instructed to call no tools, so granting it
|
|
59
|
+
* unattended write and exec buys nothing.
|
|
60
|
+
*/
|
|
61
|
+
const ARM_AUTONOMY = "full-auto";
|
|
62
|
+
const CHILD_OUTPUT_LIMIT = 4_000_000;
|
|
63
|
+
const PREVIEW_MAX_CHARS = 300;
|
|
64
|
+
const TRANSCRIPT_HEAD_CHARS = 9_000;
|
|
65
|
+
const TRANSCRIPT_TAIL_CHARS = 4_000;
|
|
66
|
+
/** Per-eval detail file written beside the standard bundle and registered in overview.files. */
|
|
67
|
+
const SKILL_EVAL_SIDECAR = "skill-eval.json";
|
|
68
|
+
|
|
69
|
+
export interface SkillsEvalOptions {
|
|
70
|
+
json: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Fixture commands in evals.md are shell authored by the skill, executed
|
|
73
|
+
* with the operator's environment. They run only on explicit opt-in;
|
|
74
|
+
* without it a fixture-bearing scenario reports an error instead of
|
|
75
|
+
* silently executing third-party shell.
|
|
76
|
+
*/
|
|
77
|
+
trustFixtures: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Let the arm runs keep the network tool plane. Off by default: an eval is
|
|
80
|
+
* a measurement of a skill against a fixed workspace, and a run that can
|
|
81
|
+
* fetch the open web is measuring something else as well.
|
|
82
|
+
*/
|
|
83
|
+
allowNetwork: boolean;
|
|
84
|
+
scenario?: string;
|
|
85
|
+
target?: string;
|
|
86
|
+
timeoutSeconds?: number;
|
|
87
|
+
workspace?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* `unmeasured` is not a verdict on the skill. It is the state of a bullet the
|
|
92
|
+
* judge never scored, which is a different fact from a bullet the judge scored
|
|
93
|
+
* negative, and reporting it as `fail` (or as `error`, which the exit code
|
|
94
|
+
* treats as failure) said the skill broke when what broke was the scoring run.
|
|
95
|
+
*/
|
|
96
|
+
type BulletVerdict = "pass" | "fail" | "error" | "unmeasured";
|
|
97
|
+
|
|
98
|
+
/** Exported for contracts tests. */
|
|
99
|
+
export interface ScoredBullet {
|
|
100
|
+
index: number;
|
|
101
|
+
text: string;
|
|
102
|
+
verdict: BulletVerdict;
|
|
103
|
+
reason: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Exported for contracts tests. */
|
|
107
|
+
export interface CapturedRun {
|
|
108
|
+
sessionId: string | null;
|
|
109
|
+
transcript: string;
|
|
110
|
+
finalText: string;
|
|
111
|
+
exitCode: number;
|
|
112
|
+
timedOut: boolean;
|
|
113
|
+
wallTimeMs: number;
|
|
114
|
+
stderr: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface ScenarioUsage {
|
|
118
|
+
tokens: number;
|
|
119
|
+
costUsd: number;
|
|
120
|
+
harness: EvalHarnessMetrics;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
interface ScenarioOutcome {
|
|
124
|
+
scenario: SkillEvalScenario;
|
|
125
|
+
bullets: ScoredBullet[];
|
|
126
|
+
baseline: CapturedRun | null;
|
|
127
|
+
treatment: CapturedRun | null;
|
|
128
|
+
judge: CapturedRun | null;
|
|
129
|
+
/** Seed workspace cloned for both arms; removed after the run, kept as a record. */
|
|
130
|
+
workspace: string;
|
|
131
|
+
wallTimeMs: number;
|
|
132
|
+
infraError: string | null;
|
|
133
|
+
usage: ScenarioUsage;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function runSkillsEvalCommand(nameOrPath: string, options: SkillsEvalOptions): Promise<number> {
|
|
137
|
+
const resolved = resolveSkillBaseDir(nameOrPath, process.cwd());
|
|
138
|
+
if (resolved.baseDir === null) {
|
|
139
|
+
printError(resolved.error ?? `unknown skill: ${nameOrPath}`);
|
|
140
|
+
return 2;
|
|
141
|
+
}
|
|
142
|
+
const list = loadSkills({ disableDiscovery: true, explicitSkillPaths: [resolved.baseDir] });
|
|
143
|
+
const skill = list.items[0];
|
|
144
|
+
if (skill === undefined) {
|
|
145
|
+
printError(`skill did not load from ${resolved.baseDir}: ${list.diagnostics.map((d) => d.message).join("; ")}`);
|
|
146
|
+
return 2;
|
|
147
|
+
}
|
|
148
|
+
// Which copy is under test, before any scenario runs. A result nobody can
|
|
149
|
+
// attribute to a source is not evidence about a skill, and several roots may
|
|
150
|
+
// carry this name.
|
|
151
|
+
process.stderr.write(
|
|
152
|
+
`clio-coder skills eval: ${skill.name} from ${resolved.origin ?? "unknown"} at ${resolved.baseDir} (sha256 ${skill.normalizedHash.slice(0, 12)}…)\n`,
|
|
153
|
+
);
|
|
154
|
+
const evalsPath = join(resolved.baseDir, "evals.md");
|
|
155
|
+
let evalsRaw: string;
|
|
156
|
+
try {
|
|
157
|
+
evalsRaw = await readFile(evalsPath, "utf8");
|
|
158
|
+
} catch {
|
|
159
|
+
printError(`skill ${skill.name} has no evals.md at ${evalsPath}`);
|
|
160
|
+
return 2;
|
|
161
|
+
}
|
|
162
|
+
const parsed = parseSkillEvals(evalsRaw);
|
|
163
|
+
for (const diagnostic of parsed.diagnostics) {
|
|
164
|
+
process.stderr.write(`clio-coder skills eval: ${diagnostic}\n`);
|
|
165
|
+
}
|
|
166
|
+
const matcher = options.scenario === undefined ? null : scenarioMatcher(options.scenario);
|
|
167
|
+
if (options.scenario !== undefined && matcher === null) {
|
|
168
|
+
printError(`invalid --scenario "${options.scenario}": use a scenario id like S1 or a bare number`);
|
|
169
|
+
return 2;
|
|
170
|
+
}
|
|
171
|
+
const scenarios = matcher === null ? parsed.scenarios : parsed.scenarios.filter(matcher);
|
|
172
|
+
if (scenarios.length === 0) {
|
|
173
|
+
printError(
|
|
174
|
+
matcher === null
|
|
175
|
+
? `no parseable scenarios in ${evalsPath}`
|
|
176
|
+
: `scenario ${options.scenario} not found in ${evalsPath} (have: ${parsed.scenarios.map((s) => s.id).join(", ") || "none"})`,
|
|
177
|
+
);
|
|
178
|
+
return 2;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
process.stderr.write(`clio-coder skills eval: ${describeArmPolicy(options.allowNetwork)}\n`);
|
|
182
|
+
const childEnv = evalChildEnv(options.allowNetwork);
|
|
183
|
+
const timeoutMs = options.timeoutSeconds !== undefined ? options.timeoutSeconds * 1000 : DEFAULT_RUN_TIMEOUT_MS;
|
|
184
|
+
const workspaceOverride = await resolveWorkspaceOverride(options.workspace);
|
|
185
|
+
if (typeof workspaceOverride !== "string" && workspaceOverride !== null) {
|
|
186
|
+
printError(workspaceOverride.error);
|
|
187
|
+
return 2;
|
|
188
|
+
}
|
|
189
|
+
const startedAt = new Date().toISOString();
|
|
190
|
+
const outcomes: ScenarioOutcome[] = [];
|
|
191
|
+
for (const scenario of scenarios) {
|
|
192
|
+
process.stderr.write(`clio-coder skills eval: ${skill.name} ${scenario.id} baseline/treatment/judge...\n`);
|
|
193
|
+
outcomes.push(
|
|
194
|
+
await runScenario(
|
|
195
|
+
skill.name,
|
|
196
|
+
resolved.baseDir,
|
|
197
|
+
scenario,
|
|
198
|
+
options.target,
|
|
199
|
+
timeoutMs,
|
|
200
|
+
workspaceOverride,
|
|
201
|
+
options.trustFixtures,
|
|
202
|
+
childEnv,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const endedAt = new Date().toISOString();
|
|
207
|
+
|
|
208
|
+
const artifact = synthesizeArtifact(skill.name, evalsPath, evalsRaw, startedAt, endedAt, outcomes, options.target);
|
|
209
|
+
let evidenceId: string | null = null;
|
|
210
|
+
let evidenceDirectory: string | null = null;
|
|
211
|
+
const evidenceErrors: string[] = [];
|
|
212
|
+
try {
|
|
213
|
+
await writeEvalArtifact(clioDataDir(), artifact);
|
|
214
|
+
const built = await buildEvalEvidence({
|
|
215
|
+
dataDir: clioDataDir(),
|
|
216
|
+
stateDir: clioStateDir(),
|
|
217
|
+
artifact,
|
|
218
|
+
sidecars: [SKILL_EVAL_SIDECAR],
|
|
219
|
+
});
|
|
220
|
+
evidenceId = built.evidenceId;
|
|
221
|
+
evidenceDirectory = built.directory;
|
|
222
|
+
await writeFile(
|
|
223
|
+
join(built.directory, SKILL_EVAL_SIDECAR),
|
|
224
|
+
`${JSON.stringify(sidecar(skill.name, artifact.evalId, outcomes, options.allowNetwork), null, 2)}\n`,
|
|
225
|
+
"utf8",
|
|
226
|
+
);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
evidenceErrors.push(error instanceof Error ? error.message : String(error));
|
|
229
|
+
}
|
|
230
|
+
for (const message of evidenceErrors) {
|
|
231
|
+
process.stderr.write(`clio-coder skills eval: evidence build failed: ${message}\n`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (options.json) {
|
|
235
|
+
for (const outcome of outcomes) {
|
|
236
|
+
for (const bullet of outcome.bullets) {
|
|
237
|
+
process.stdout.write(
|
|
238
|
+
`${JSON.stringify({
|
|
239
|
+
schema: "experimental",
|
|
240
|
+
kind: "skill-eval-bullet",
|
|
241
|
+
skill: skill.name,
|
|
242
|
+
scenario: outcome.scenario.id,
|
|
243
|
+
title: outcome.scenario.title,
|
|
244
|
+
bullet: bullet.index,
|
|
245
|
+
expected: bullet.text,
|
|
246
|
+
verdict: bullet.verdict,
|
|
247
|
+
reason: bullet.reason,
|
|
248
|
+
network: networkPolicyLabel(options.allowNetwork),
|
|
249
|
+
autonomy: ARM_AUTONOMY,
|
|
250
|
+
baselineSessionId: outcome.baseline?.sessionId ?? null,
|
|
251
|
+
treatmentSessionId: outcome.treatment?.sessionId ?? null,
|
|
252
|
+
judgeSessionId: outcome.judge?.sessionId ?? null,
|
|
253
|
+
evalId: artifact.evalId,
|
|
254
|
+
evidenceId,
|
|
255
|
+
})}\n`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
printHumanReport(skill.name, outcomes, artifact.evalId, evidenceId, evidenceDirectory, options.allowNetwork);
|
|
261
|
+
}
|
|
262
|
+
const anyFailure = outcomes.some((outcome) =>
|
|
263
|
+
outcome.bullets.some((bullet) => bullet.verdict === "fail" || bullet.verdict === "error"),
|
|
264
|
+
);
|
|
265
|
+
const anyUnmeasured = outcomes.some((outcome) => outcome.bullets.some((bullet) => bullet.verdict === "unmeasured"));
|
|
266
|
+
// 1 means the skill failed its rubric. 3 means no rubric verdict stands to
|
|
267
|
+
// regress against, either because a scenario went unmeasured or because the
|
|
268
|
+
// evidence archive write failed; a caller branching on the code must not
|
|
269
|
+
// read either as a regression, and a run that measured nothing is still not
|
|
270
|
+
// a pass.
|
|
271
|
+
if (anyFailure) return 1;
|
|
272
|
+
return anyUnmeasured || evidenceErrors.length > 0 ? 3 : 0;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export type NetworkPolicyLabel = "hermetic" | "allowed";
|
|
276
|
+
|
|
277
|
+
function networkPolicyLabel(allowNetwork: boolean): NetworkPolicyLabel {
|
|
278
|
+
return allowNetwork ? "allowed" : "hermetic";
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* One line stating everything the harness imposes on its children, before the
|
|
283
|
+
* first arm starts. Autonomy sits beside the network mode because they are the
|
|
284
|
+
* same kind of fact: a reader who has to guess either one cannot tell what a
|
|
285
|
+
* verdict was measured under.
|
|
286
|
+
*/
|
|
287
|
+
function describeArmPolicy(allowNetwork: boolean): string {
|
|
288
|
+
const network = allowNetwork
|
|
289
|
+
? "network allowed (--allow-network), so arm runs keep the web tools"
|
|
290
|
+
: "network hermetic, so the web tools are stripped from every arm run (pass --allow-network to keep them)";
|
|
291
|
+
return `policy: autonomy ${ARM_AUTONOMY} for the baseline and treatment arms in per-run disposable workspaces; ${network}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** The same two facts in the past tense, for the tail of the human report. */
|
|
295
|
+
function describeArmPolicyOutcome(allowNetwork: boolean): string {
|
|
296
|
+
const network = allowNetwork
|
|
297
|
+
? "network: allowed (--allow-network); arm runs kept the web tools"
|
|
298
|
+
: "network: hermetic; the web tools were stripped from every arm run";
|
|
299
|
+
return `policy: the baseline and treatment arms ran at autonomy ${ARM_AUTONOMY}; ${network}`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
type EvalArm = "baseline" | "treatment" | "judge";
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* The argv for one arm's child `clio-coder run`. Every arm streams terminal JSON
|
|
306
|
+
* events (a turn that ends on a terminating tool carries its content there,
|
|
307
|
+
* not in text mode) and runs with discovery off so only the explicit --skill
|
|
308
|
+
* of the treatment arm loads.
|
|
309
|
+
*
|
|
310
|
+
* @internal Exported for contract tests.
|
|
311
|
+
*/
|
|
312
|
+
export function armRunArgs(
|
|
313
|
+
arm: EvalArm,
|
|
314
|
+
prompt: string,
|
|
315
|
+
options: { target?: string | undefined; skillBaseDir?: string | undefined } = {},
|
|
316
|
+
): string[] {
|
|
317
|
+
const args = ["run", "--json", "--json-events", "terminal", "--no-skills"];
|
|
318
|
+
if (arm !== "judge") args.push("--autonomy", ARM_AUTONOMY);
|
|
319
|
+
if (options.skillBaseDir !== undefined) args.push("--skill", options.skillBaseDir);
|
|
320
|
+
if (options.target !== undefined) args.push("--target", options.target);
|
|
321
|
+
args.push(prompt);
|
|
322
|
+
return args;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* The harness's own permission gate, found in an arm transcript.
|
|
327
|
+
*
|
|
328
|
+
* A headless run denies any call it cannot get an operator's approval for, and
|
|
329
|
+
* the denial reaches the model as a tool error. An arm that collected those is
|
|
330
|
+
* not evidence about the skill in either direction: it never ran the skill's
|
|
331
|
+
* workflow, so its bullets are unmeasured, exactly like a judge that never
|
|
332
|
+
* scored them. Reported as an infra error, never as a verdict.
|
|
333
|
+
*
|
|
334
|
+
* @internal Exported for contract tests.
|
|
335
|
+
*/
|
|
336
|
+
export function permissionWallReason(arm: EvalArm, run: CapturedRun): string | null {
|
|
337
|
+
const haystack = `${run.transcript}\n${run.finalText}`;
|
|
338
|
+
let blocked = 0;
|
|
339
|
+
let at = haystack.indexOf(HEADLESS_PERMISSION_DENIED_MARKER);
|
|
340
|
+
while (at >= 0) {
|
|
341
|
+
blocked += 1;
|
|
342
|
+
at = haystack.indexOf(HEADLESS_PERMISSION_DENIED_MARKER, at + HEADLESS_PERMISSION_DENIED_MARKER.length);
|
|
343
|
+
}
|
|
344
|
+
if (blocked === 0) return null;
|
|
345
|
+
return `${arm} arm transcript carries the headless permission wall ${blocked} time(s) ("${HEADLESS_PERMISSION_DENIED_MARKER}"): this run measured the harness's own autonomy gate, not the skill`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function unmeasuredBullets(scenario: SkillEvalScenario, reason: string): ScoredBullet[] {
|
|
349
|
+
return scenario.expected.map((text, index) => ({ index: index + 1, text, verdict: "unmeasured", reason }));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* The environment each arm's child `clio-coder run` inherits. Absent
|
|
354
|
+
* `--allow-network` the hermetic switch is set, which strips the RETRIEVE
|
|
355
|
+
* plane from every registry the child and its descendants build. With the flag
|
|
356
|
+
* the variable is removed rather than left alone, so an ambient setting in the
|
|
357
|
+
* operator's shell cannot make `--allow-network` a claim the run does not keep.
|
|
358
|
+
*
|
|
359
|
+
* @internal Exported for contract tests.
|
|
360
|
+
*/
|
|
361
|
+
export function evalChildEnv(allowNetwork: boolean, env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
362
|
+
const childEnv: NodeJS.ProcessEnv = { ...env };
|
|
363
|
+
if (allowNetwork) Reflect.deleteProperty(childEnv, NO_NETWORK_TOOLS_ENV);
|
|
364
|
+
else childEnv[NO_NETWORK_TOOLS_ENV] = "1";
|
|
365
|
+
return childEnv;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The copy of a named skill this eval will actually execute.
|
|
370
|
+
*
|
|
371
|
+
* Resolution follows activation, not the catalog. A bare name previously
|
|
372
|
+
* resolved straight to the catalog entry, so an operator with the same skill
|
|
373
|
+
* installed in a project or user root evaluated a different artifact from the
|
|
374
|
+
* one their agent would load, and nothing in the run said which. Discovery runs
|
|
375
|
+
* first and its precedence winner is exactly what an activation would select;
|
|
376
|
+
* the catalog answers only for a name no root installed, which is the skill
|
|
377
|
+
* author's case in this repository.
|
|
378
|
+
*/
|
|
379
|
+
export function resolveSkillBaseDir(
|
|
380
|
+
nameOrPath: string,
|
|
381
|
+
cwd: string,
|
|
382
|
+
): { baseDir: string | null; origin?: string; error?: string } {
|
|
383
|
+
const asPath = resolve(nameOrPath);
|
|
384
|
+
if (existsSync(join(asPath, "SKILL.md"))) return { baseDir: asPath, origin: "path" };
|
|
385
|
+
const discovered = loadSkills({ cwd }).items.find((skill) => skill.name === nameOrPath);
|
|
386
|
+
if (discovered !== undefined) {
|
|
387
|
+
return { baseDir: discovered.baseDir, origin: `${discovered.source}/${discovered.scope}` };
|
|
388
|
+
}
|
|
389
|
+
const marketplace = discoverMarketplaceSkills({ cwd });
|
|
390
|
+
const entry = marketplace.skills.find((skill) => skill.name === nameOrPath && skill.origin === "catalog");
|
|
391
|
+
if (entry !== undefined) return { baseDir: entry.sourceUrl, origin: "catalog" };
|
|
392
|
+
return {
|
|
393
|
+
baseDir: null,
|
|
394
|
+
error: `skill not found: ${nameOrPath} (expected an installed skill, a catalog skill name, or a directory containing SKILL.md)`,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* A full id like "D2" selects exactly that scenario; a bare number selects
|
|
400
|
+
* every scenario with that number (evals.md files may mix letter prefixes).
|
|
401
|
+
*/
|
|
402
|
+
function scenarioMatcher(value: string): ((scenario: SkillEvalScenario) => boolean) | null {
|
|
403
|
+
const trimmed = value.trim();
|
|
404
|
+
const full = /^([A-Za-z])(\d+)$/.exec(trimmed);
|
|
405
|
+
if (full !== null) {
|
|
406
|
+
const id = `${(full[1] ?? "").toUpperCase()}${Number.parseInt(full[2] ?? "", 10)}`;
|
|
407
|
+
return (scenario) => scenario.id === id;
|
|
408
|
+
}
|
|
409
|
+
const bare = /^(\d+)$/.exec(trimmed);
|
|
410
|
+
if (bare !== null) {
|
|
411
|
+
const wanted = Number.parseInt(bare[1] ?? "", 10);
|
|
412
|
+
return (scenario) => scenario.number === wanted;
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async function resolveWorkspaceOverride(workspace: string | undefined): Promise<string | null | { error: string }> {
|
|
418
|
+
if (workspace === undefined) return null;
|
|
419
|
+
const resolved = resolve(workspace);
|
|
420
|
+
try {
|
|
421
|
+
const info = await stat(resolved);
|
|
422
|
+
if (!info.isDirectory()) return { error: `--workspace is not a directory: ${resolved}` };
|
|
423
|
+
return resolved;
|
|
424
|
+
} catch (error) {
|
|
425
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
426
|
+
return { error: `--workspace is not readable: ${resolved}: ${detail}` };
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function runScenario(
|
|
431
|
+
skillName: string,
|
|
432
|
+
skillBaseDir: string,
|
|
433
|
+
scenario: SkillEvalScenario,
|
|
434
|
+
target: string | undefined,
|
|
435
|
+
timeoutMs: number,
|
|
436
|
+
workspaceOverride: string | null,
|
|
437
|
+
trustFixtures: boolean,
|
|
438
|
+
childEnv: NodeJS.ProcessEnv,
|
|
439
|
+
): Promise<ScenarioOutcome> {
|
|
440
|
+
// Published per-scenario figure: monotonic so a clock correction during a
|
|
441
|
+
// long sweep cannot land in one row's wall time.
|
|
442
|
+
const scenarioStart = performance.now();
|
|
443
|
+
const workspace = await mkdtemp(join(tmpdir(), "clio-skill-eval-seed-"));
|
|
444
|
+
let runWorkspaces: MaterializedSkillEvalWorkspaces | null = null;
|
|
445
|
+
try {
|
|
446
|
+
if (workspaceOverride !== null) await copyWorkspace(workspaceOverride, workspace);
|
|
447
|
+
const fixtureError = await runFixtureCommands(scenario, workspace, timeoutMs, trustFixtures);
|
|
448
|
+
if (fixtureError !== null) {
|
|
449
|
+
return await completeScenarioOutcome({
|
|
450
|
+
scenario,
|
|
451
|
+
bullets: errorBullets(scenario, fixtureError),
|
|
452
|
+
baseline: null,
|
|
453
|
+
treatment: null,
|
|
454
|
+
judge: null,
|
|
455
|
+
workspace,
|
|
456
|
+
wallTimeMs: Math.round(performance.now() - scenarioStart),
|
|
457
|
+
infraError: fixtureError,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
runWorkspaces = await materializeSkillEvalWorkspaces(workspace);
|
|
461
|
+
const baseline = await captureHeadlessRun(
|
|
462
|
+
armRunArgs("baseline", scenario.setup, { target }),
|
|
463
|
+
runWorkspaces.baseline,
|
|
464
|
+
timeoutMs,
|
|
465
|
+
childEnv,
|
|
466
|
+
);
|
|
467
|
+
const treatment = await captureHeadlessRun(
|
|
468
|
+
armRunArgs("treatment", `/skill:${skillName} ${scenario.setup}`, { target, skillBaseDir }),
|
|
469
|
+
runWorkspaces.treatment,
|
|
470
|
+
timeoutMs,
|
|
471
|
+
childEnv,
|
|
472
|
+
);
|
|
473
|
+
const infra = runInfraError("baseline", baseline) ?? runInfraError("treatment", treatment);
|
|
474
|
+
if (infra !== null) {
|
|
475
|
+
return await completeScenarioOutcome({
|
|
476
|
+
scenario,
|
|
477
|
+
bullets: errorBullets(scenario, infra),
|
|
478
|
+
baseline,
|
|
479
|
+
treatment,
|
|
480
|
+
judge: null,
|
|
481
|
+
workspace,
|
|
482
|
+
wallTimeMs: Math.round(performance.now() - scenarioStart),
|
|
483
|
+
infraError: infra,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
// An arm the harness itself blocked is scored by nobody: spending a judge
|
|
487
|
+
// run on those transcripts would only buy well-reasoned verdicts about
|
|
488
|
+
// the permission wall.
|
|
489
|
+
const wall = permissionWallReason("baseline", baseline) ?? permissionWallReason("treatment", treatment);
|
|
490
|
+
if (wall !== null) {
|
|
491
|
+
return await completeScenarioOutcome({
|
|
492
|
+
scenario,
|
|
493
|
+
bullets: unmeasuredBullets(scenario, wall),
|
|
494
|
+
baseline,
|
|
495
|
+
treatment,
|
|
496
|
+
judge: null,
|
|
497
|
+
workspace,
|
|
498
|
+
wallTimeMs: Math.round(performance.now() - scenarioStart),
|
|
499
|
+
infraError: wall,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
// The judge also runs with --json: a model that ends the turn through a
|
|
503
|
+
// terminating tool (artifact plan/review/report) prints only that tool's
|
|
504
|
+
// result line in text mode, while the event stream carries the artifact
|
|
505
|
+
// content the verdict may live in.
|
|
506
|
+
const judge = await captureHeadlessRun(
|
|
507
|
+
armRunArgs("judge", judgePrompt(scenario, baseline.transcript, treatment.transcript), { target }),
|
|
508
|
+
runWorkspaces.judge,
|
|
509
|
+
timeoutMs,
|
|
510
|
+
childEnv,
|
|
511
|
+
);
|
|
512
|
+
const judgeInfra = runInfraError("judge", judge);
|
|
513
|
+
if (judgeInfra !== null) {
|
|
514
|
+
return await completeScenarioOutcome({
|
|
515
|
+
scenario,
|
|
516
|
+
bullets: errorBullets(scenario, judgeInfra),
|
|
517
|
+
baseline,
|
|
518
|
+
treatment,
|
|
519
|
+
judge,
|
|
520
|
+
workspace,
|
|
521
|
+
wallTimeMs: Math.round(performance.now() - scenarioStart),
|
|
522
|
+
infraError: judgeInfra,
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
const bullets = parseJudgeVerdicts(scenario, judge);
|
|
526
|
+
return await completeScenarioOutcome({
|
|
527
|
+
scenario,
|
|
528
|
+
bullets,
|
|
529
|
+
baseline,
|
|
530
|
+
treatment,
|
|
531
|
+
judge,
|
|
532
|
+
workspace,
|
|
533
|
+
wallTimeMs: Math.round(performance.now() - scenarioStart),
|
|
534
|
+
infraError: null,
|
|
535
|
+
});
|
|
536
|
+
} finally {
|
|
537
|
+
try {
|
|
538
|
+
if (runWorkspaces !== null) await runWorkspaces.cleanup();
|
|
539
|
+
} finally {
|
|
540
|
+
await rm(workspace, { recursive: true, force: true });
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export interface MaterializedSkillEvalWorkspaces {
|
|
546
|
+
baseline: string;
|
|
547
|
+
treatment: string;
|
|
548
|
+
judge: string;
|
|
549
|
+
cleanup(): Promise<void>;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* One private root per arm, with the arm's workspace nested inside it.
|
|
554
|
+
*
|
|
555
|
+
* The arms used to be three `mkdtemp` directories sitting side by side in the
|
|
556
|
+
* system temp dir, named `...-baseline-`, `...-treatment-` and `...-judge-`.
|
|
557
|
+
* A full-auto run in one arm could therefore reach the others in a single
|
|
558
|
+
* relative step, and the names said which was which, so a treatment run could
|
|
559
|
+
* read the baseline it is supposed to be measured against. Nesting each
|
|
560
|
+
* workspace one level down under a neutrally-named private root means `..`
|
|
561
|
+
* from a workspace shows only that arm.
|
|
562
|
+
*
|
|
563
|
+
* This raises the cost of crossing arms; it is not containment. Nothing here
|
|
564
|
+
* confines a run to its workspace, because the write boundary is a per-run
|
|
565
|
+
* tool policy and not something the eval harness can impose on a child
|
|
566
|
+
* process it spawns. An arm that walks the system temp dir can still find the
|
|
567
|
+
* others. Treat eval results from a full-auto run as evidence about a
|
|
568
|
+
* cooperative model, not as an isolation guarantee.
|
|
569
|
+
*/
|
|
570
|
+
async function armWorkspace(created: string[]): Promise<string> {
|
|
571
|
+
const root = await mkdtemp(join(tmpdir(), "clio-skill-eval-"));
|
|
572
|
+
created.push(root);
|
|
573
|
+
const workspace = join(root, "workspace");
|
|
574
|
+
await mkdir(workspace, { recursive: true });
|
|
575
|
+
return workspace;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** @internal Exported for contract tests. */
|
|
579
|
+
export async function materializeSkillEvalWorkspaces(seedWorkspace: string): Promise<MaterializedSkillEvalWorkspaces> {
|
|
580
|
+
const created: string[] = [];
|
|
581
|
+
try {
|
|
582
|
+
const baseline = await armWorkspace(created);
|
|
583
|
+
const treatment = await armWorkspace(created);
|
|
584
|
+
const judge = await armWorkspace(created);
|
|
585
|
+
await Promise.all([copyWorkspace(seedWorkspace, baseline), copyWorkspace(seedWorkspace, treatment)]);
|
|
586
|
+
return {
|
|
587
|
+
baseline,
|
|
588
|
+
treatment,
|
|
589
|
+
judge,
|
|
590
|
+
cleanup: async () => {
|
|
591
|
+
await Promise.all(created.map((path) => rm(path, { recursive: true, force: true })));
|
|
592
|
+
},
|
|
593
|
+
};
|
|
594
|
+
} catch (error) {
|
|
595
|
+
await Promise.all(created.map((path) => rm(path, { recursive: true, force: true })));
|
|
596
|
+
throw error;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async function copyWorkspace(source: string, destination: string): Promise<void> {
|
|
601
|
+
await cp(source, destination, {
|
|
602
|
+
recursive: true,
|
|
603
|
+
preserveTimestamps: true,
|
|
604
|
+
verbatimSymlinks: true,
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
async function completeScenarioOutcome(outcome: Omit<ScenarioOutcome, "usage">): Promise<ScenarioOutcome> {
|
|
609
|
+
return {
|
|
610
|
+
...outcome,
|
|
611
|
+
usage: await usageForCapturedRuns([outcome.baseline, outcome.treatment, outcome.judge]),
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
async function runFixtureCommands(
|
|
616
|
+
scenario: SkillEvalScenario,
|
|
617
|
+
workspace: string,
|
|
618
|
+
timeoutMs: number,
|
|
619
|
+
trustFixtures: boolean,
|
|
620
|
+
): Promise<string | null> {
|
|
621
|
+
const commands = scenario.fixtureCommands?.trim();
|
|
622
|
+
if (commands === undefined || commands.length === 0) return null;
|
|
623
|
+
if (!trustFixtures) {
|
|
624
|
+
return "scenario declares fixture commands, which are real shell from the skill's evals.md; review them and rerun with --trust-fixtures";
|
|
625
|
+
}
|
|
626
|
+
const validationError = validateFixtureCommands(commands);
|
|
627
|
+
if (validationError !== null) return `fixture setup rejected: ${validationError}`;
|
|
628
|
+
const result = await runBashCommand(commands, {
|
|
629
|
+
cwd: workspace,
|
|
630
|
+
timeoutMs: Math.min(timeoutMs, 120_000),
|
|
631
|
+
});
|
|
632
|
+
const output = combineBashOutput(result).trim();
|
|
633
|
+
if (result.timedOut) return `fixture setup timed out${output.length > 0 ? `: ${truncate(output, 300)}` : ""}`;
|
|
634
|
+
if (result.outputCapped) {
|
|
635
|
+
return `fixture setup output exceeded limit${output.length > 0 ? `: ${truncate(output, 300)}` : ""}`;
|
|
636
|
+
}
|
|
637
|
+
if (result.error !== null) {
|
|
638
|
+
return `fixture setup failed${output.length > 0 ? `: ${truncate(output, 300)}` : `: ${result.error.message}`}`;
|
|
639
|
+
}
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Best-effort lint over trusted fixture scripts, not a sandbox: fixtures only
|
|
645
|
+
* run behind --trust-fixtures, and these checks exist to catch obvious
|
|
646
|
+
* workspace escapes in otherwise-reviewed scripts (shell is not reliably
|
|
647
|
+
* classifiable by regex).
|
|
648
|
+
*/
|
|
649
|
+
function validateFixtureCommands(commands: string): string | null {
|
|
650
|
+
const checks: Array<{ pattern: RegExp; reason: string }> = [
|
|
651
|
+
{
|
|
652
|
+
pattern: /(^|[\s;&|(<>='"`])\/(?!dev\/null(?:\s|$))(?=\S)/,
|
|
653
|
+
reason: "absolute paths are not allowed in fixture commands",
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
pattern: /(^|[\s;&|()'"`/=])\.\.(?=$|[/\s;&|()'"`])/,
|
|
657
|
+
reason: "parent-directory path segments are not allowed in fixture commands",
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
pattern: /(^|[\s;&|()])~(?=$|[/\s;&|()])/,
|
|
661
|
+
reason: "home-directory expansion is not allowed in fixture commands",
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
pattern:
|
|
665
|
+
/\$(?:\{(?:HOME|CLIO_CODER_HOME|CLIO_CODER_CONFIG_DIR|CLIO_CODER_DATA_DIR|CLIO_CODER_STATE_DIR|CLIO_CODER_CACHE_DIR)\}|(?:HOME|CLIO_CODER_HOME|CLIO_CODER_CONFIG_DIR|CLIO_CODER_DATA_DIR|CLIO_CODER_STATE_DIR|CLIO_CODER_CACHE_DIR)\b)/,
|
|
666
|
+
reason: "home and Clio directory environment variables are not allowed in fixture commands",
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
pattern: /(^|[\s;&|()])(?:cd|pushd|popd)\b/,
|
|
670
|
+
reason: "directory-changing commands are not allowed in fixture commands",
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
pattern: /(^|[\s;&|()])(?:sudo|su|ssh|scp|rsync)\b/,
|
|
674
|
+
reason: "privilege-changing or remote commands are not allowed in fixture commands",
|
|
675
|
+
},
|
|
676
|
+
];
|
|
677
|
+
for (const check of checks) {
|
|
678
|
+
if (check.pattern.test(commands)) return check.reason;
|
|
679
|
+
}
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
async function usageForCapturedRuns(runs: ReadonlyArray<CapturedRun | null>): Promise<ScenarioUsage> {
|
|
684
|
+
const sessionIds = new Set(
|
|
685
|
+
runs.flatMap((run) => (run?.sessionId !== null && run?.sessionId !== undefined ? [run.sessionId] : [])),
|
|
686
|
+
);
|
|
687
|
+
const usage: ScenarioUsage = {
|
|
688
|
+
tokens: 0,
|
|
689
|
+
costUsd: 0,
|
|
690
|
+
harness: { ...ZERO_EVAL_HARNESS_METRICS },
|
|
691
|
+
};
|
|
692
|
+
if (sessionIds.size === 0) return usage;
|
|
693
|
+
const receiptRoot = join(clioStateDir(), "receipts");
|
|
694
|
+
let files: string[];
|
|
695
|
+
try {
|
|
696
|
+
files = (await readdir(receiptRoot)).filter((name) => name.endsWith(".json"));
|
|
697
|
+
} catch {
|
|
698
|
+
return usage;
|
|
699
|
+
}
|
|
700
|
+
for (const file of files) {
|
|
701
|
+
let parsed: unknown;
|
|
702
|
+
try {
|
|
703
|
+
parsed = JSON.parse(await readFile(join(receiptRoot, file), "utf8"));
|
|
704
|
+
} catch {
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (!isRecord(parsed) || typeof parsed.sessionId !== "string" || !sessionIds.has(parsed.sessionId)) continue;
|
|
708
|
+
usage.tokens += readNumber(parsed.tokenCount);
|
|
709
|
+
usage.costUsd += readNumber(parsed.costUsd);
|
|
710
|
+
usage.harness.receiptCount += 1;
|
|
711
|
+
usage.harness.toolCalls += readNumber(parsed.toolCalls);
|
|
712
|
+
if (isRecord(parsed.safety) && isRecord(parsed.safety.decisions)) {
|
|
713
|
+
usage.harness.safetyBlocks += readNumber(parsed.safety.decisions.blocked);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return usage;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function runInfraError(label: string, run: CapturedRun): string | null {
|
|
720
|
+
if (run.timedOut) return `${label} run timed out`;
|
|
721
|
+
if (run.exitCode !== 0) {
|
|
722
|
+
const detail = run.stderr.trim().split("\n").at(-1) ?? "";
|
|
723
|
+
return `${label} run exited ${run.exitCode}${detail.length > 0 ? `: ${detail}` : ""}`;
|
|
724
|
+
}
|
|
725
|
+
if (run.transcript.trim().length === 0 && run.finalText.trim().length === 0) {
|
|
726
|
+
return `${label} run produced no transcript`;
|
|
727
|
+
}
|
|
728
|
+
return null;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function errorBullets(scenario: SkillEvalScenario, reason: string): ScoredBullet[] {
|
|
732
|
+
return scenario.expected.map((text, index) => ({ index: index + 1, text, verdict: "error", reason }));
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function captureHeadlessRun(
|
|
736
|
+
args: ReadonlyArray<string>,
|
|
737
|
+
cwd: string,
|
|
738
|
+
timeoutMs: number,
|
|
739
|
+
env: NodeJS.ProcessEnv,
|
|
740
|
+
): Promise<CapturedRun> {
|
|
741
|
+
const startedMs = performance.now();
|
|
742
|
+
return new Promise((resolvePromise) => {
|
|
743
|
+
let stdout = "";
|
|
744
|
+
let pendingStdout = "";
|
|
745
|
+
let stderr = "";
|
|
746
|
+
let timedOut = false;
|
|
747
|
+
let settled = false;
|
|
748
|
+
const keepLine = (line: string): void => {
|
|
749
|
+
if (line.length === 0) return;
|
|
750
|
+
if (stdout.length < CHILD_OUTPUT_LIMIT) stdout += `${line}\n`;
|
|
751
|
+
};
|
|
752
|
+
const child = spawn(process.execPath, [process.argv[1] ?? "", ...args], {
|
|
753
|
+
cwd,
|
|
754
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
755
|
+
env,
|
|
756
|
+
});
|
|
757
|
+
child.stdout.setEncoding("utf8");
|
|
758
|
+
child.stderr.setEncoding("utf8");
|
|
759
|
+
child.stdout.on("data", (chunk: string) => {
|
|
760
|
+
pendingStdout += chunk;
|
|
761
|
+
const lines = pendingStdout.split("\n");
|
|
762
|
+
pendingStdout = lines.pop() ?? "";
|
|
763
|
+
for (const line of lines) keepLine(line);
|
|
764
|
+
});
|
|
765
|
+
child.stderr.on("data", (chunk: string) => {
|
|
766
|
+
if (stderr.length < CHILD_OUTPUT_LIMIT) stderr += chunk;
|
|
767
|
+
});
|
|
768
|
+
let killTimer: NodeJS.Timeout | undefined;
|
|
769
|
+
const timer = setTimeout(() => {
|
|
770
|
+
timedOut = true;
|
|
771
|
+
child.kill("SIGTERM");
|
|
772
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), 5_000);
|
|
773
|
+
}, timeoutMs);
|
|
774
|
+
const finish = (exitCode: number): void => {
|
|
775
|
+
if (settled) return;
|
|
776
|
+
settled = true;
|
|
777
|
+
clearTimeout(timer);
|
|
778
|
+
if (killTimer !== undefined) clearTimeout(killTimer);
|
|
779
|
+
keepLine(pendingStdout);
|
|
780
|
+
pendingStdout = "";
|
|
781
|
+
const parsedRun = parseRunStdout(stdout);
|
|
782
|
+
resolvePromise({
|
|
783
|
+
...parsedRun,
|
|
784
|
+
exitCode,
|
|
785
|
+
timedOut,
|
|
786
|
+
wallTimeMs: Math.round(performance.now() - startedMs),
|
|
787
|
+
stderr,
|
|
788
|
+
});
|
|
789
|
+
};
|
|
790
|
+
child.on("error", (error) => {
|
|
791
|
+
stderr += `\n${error.message}`;
|
|
792
|
+
finish(1);
|
|
793
|
+
});
|
|
794
|
+
child.on("close", (code) => finish(typeof code === "number" ? code : timedOut ? 124 : 1));
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Fold a headless run's stdout into a compact transcript. `--json` runs emit
|
|
800
|
+
* one terminal JSONL event per line (session header, message_end,
|
|
801
|
+
* tool_execution_*, turn_start, turn_end);
|
|
802
|
+
* text-mode runs emit the final assistant text, which passes through as-is.
|
|
803
|
+
*/
|
|
804
|
+
/**
|
|
805
|
+
* True for the `context` call that delivers a skill's body:
|
|
806
|
+
* `context(scope="skills", name=<skill>)`. Without a name the same scope only
|
|
807
|
+
* lists what is available, which carries no instructions.
|
|
808
|
+
*/
|
|
809
|
+
function loadsSkillBody(tool: string, args: unknown): boolean {
|
|
810
|
+
if (tool !== "context" || !isRecord(args)) return false;
|
|
811
|
+
return args.scope === "skills" && typeof args.name === "string" && args.name.trim().length > 0;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/** @internal Exported for contract tests. */
|
|
815
|
+
export function parseRunStdout(stdout: string): { sessionId: string | null; transcript: string; finalText: string } {
|
|
816
|
+
let sessionId: string | null = null;
|
|
817
|
+
const lines: string[] = [];
|
|
818
|
+
let finalText = "";
|
|
819
|
+
let sawJson = false;
|
|
820
|
+
// Tool calls whose result is the skill's own SKILL.md. Correlated by
|
|
821
|
+
// toolCallId, which both the start and end events carry.
|
|
822
|
+
const skillBodyCallIds = new Set<string>();
|
|
823
|
+
for (const line of stdout.split("\n")) {
|
|
824
|
+
if (line.trim().length === 0) continue;
|
|
825
|
+
let event: unknown;
|
|
826
|
+
try {
|
|
827
|
+
event = JSON.parse(line);
|
|
828
|
+
} catch {
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
if (!isRecord(event)) continue;
|
|
832
|
+
sawJson = true;
|
|
833
|
+
if (event.type === "session" && typeof event.id === "string") {
|
|
834
|
+
sessionId = event.id;
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
if (event.type === "tool_execution_start") {
|
|
838
|
+
const tool = readString(event.toolName) ?? readString(event.tool) ?? "tool";
|
|
839
|
+
const args = event.args ?? event.arguments ?? event.input;
|
|
840
|
+
const callId = readString(event.toolCallId);
|
|
841
|
+
if (callId !== null && loadsSkillBody(tool, args)) skillBodyCallIds.add(callId);
|
|
842
|
+
lines.push(`TOOL ${tool} args=${preview(args)}`);
|
|
843
|
+
// Terminating tools end the turn with no assistant text; their content
|
|
844
|
+
// IS the answer, so keep it whole (the judge verdict may live here).
|
|
845
|
+
if (tool === "artifact" && isRecord(args) && typeof args.content === "string") {
|
|
846
|
+
lines.push(`TERMINAL ${tool} content:\n${args.content}`);
|
|
847
|
+
}
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
if (event.type === "tool_execution_end") {
|
|
851
|
+
const tool = readString(event.toolName) ?? readString(event.tool) ?? "tool";
|
|
852
|
+
const status = event.isError === true ? "error" : "ok";
|
|
853
|
+
const callId = readString(event.toolCallId);
|
|
854
|
+
// The skill body is the instructions, not the behavior. Left in the
|
|
855
|
+
// transcript it is the easiest thing in the run for a judge to quote,
|
|
856
|
+
// and a 30B judge scored bullets as passing from SKILL.md prose that
|
|
857
|
+
// the model had only read, never acted on. The load still shows, so
|
|
858
|
+
// "did it load the skill" stays checkable.
|
|
859
|
+
if (callId !== null && skillBodyCallIds.has(callId)) {
|
|
860
|
+
lines.push(`RESULT ${tool} ${status}: <skill body withheld from the judge>`);
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
lines.push(`RESULT ${tool} ${status}: ${preview(event.result)}`);
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
if (event.type === "message_end" && isRecord(event.message) && event.message.role === "assistant") {
|
|
867
|
+
const content = Array.isArray(event.message.content) ? event.message.content : [];
|
|
868
|
+
const text = content
|
|
869
|
+
.filter((item): item is { type: "text"; text: string } => {
|
|
870
|
+
return isRecord(item) && item.type === "text" && typeof item.text === "string";
|
|
871
|
+
})
|
|
872
|
+
.map((item) => item.text)
|
|
873
|
+
.join("")
|
|
874
|
+
.trim();
|
|
875
|
+
if (text.length > 0) {
|
|
876
|
+
lines.push(`ASSISTANT: ${text}`);
|
|
877
|
+
finalText = text;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (!sawJson) {
|
|
882
|
+
const text = stdout.trim();
|
|
883
|
+
return { sessionId: null, transcript: text, finalText: text };
|
|
884
|
+
}
|
|
885
|
+
return { sessionId, transcript: elide(lines.join("\n")), finalText };
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function judgePrompt(scenario: SkillEvalScenario, baselineTranscript: string, treatmentTranscript: string): string {
|
|
889
|
+
const bullets = scenario.expected.map((text, index) => `${index + 1}. ${text}`).join("\n");
|
|
890
|
+
return [
|
|
891
|
+
"You are scoring a skill evaluation. Two transcripts follow: BASELINE ran without the skill, TREATMENT ran with the skill loaded.",
|
|
892
|
+
"Score each EXPECTED bullet strictly against the TREATMENT transcript; the baseline exists only for gap context.",
|
|
893
|
+
"A bullet passes only if the treatment transcript observably satisfies it; anything unverifiable from the transcript fails.",
|
|
894
|
+
'Reply with STRICT JSON only, no prose and no code fences, exactly: {"bullets":[{"index":1,"pass":true,"reason":"<= 25 words"}]}',
|
|
895
|
+
`Include one entry per bullet, indexes 1 through ${scenario.expected.length} in order.`,
|
|
896
|
+
"Do not use any tools. Respond with the JSON verdict directly.",
|
|
897
|
+
"",
|
|
898
|
+
`SCENARIO ${scenario.id} - ${scenario.title}`,
|
|
899
|
+
`SETUP: ${scenario.setup}`,
|
|
900
|
+
"",
|
|
901
|
+
"EXPECTED BULLETS:",
|
|
902
|
+
bullets,
|
|
903
|
+
"",
|
|
904
|
+
"BASELINE TRANSCRIPT:",
|
|
905
|
+
baselineTranscript.length > 0 ? baselineTranscript : "(empty)",
|
|
906
|
+
"",
|
|
907
|
+
"TREATMENT TRANSCRIPT:",
|
|
908
|
+
treatmentTranscript.length > 0 ? treatmentTranscript : "(empty)",
|
|
909
|
+
].join("\n");
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Why a judge response carried no verdict, named after the thing that failed.
|
|
914
|
+
* A response that opened a bullets object and never closed it is the observed
|
|
915
|
+
* truncation on small local models; a response with no bullets key at all
|
|
916
|
+
* never started one. Neither is evidence about the skill.
|
|
917
|
+
*/
|
|
918
|
+
function judgeVerdictAbsenceReason(judge: CapturedRun): string {
|
|
919
|
+
const sawBulletsKey = `${judge.finalText}\n${judge.transcript}`.includes('"bullets"');
|
|
920
|
+
return sawBulletsKey
|
|
921
|
+
? "judge response truncated mid-verdict (no complete bullets object): bullet not scored"
|
|
922
|
+
: "judge response carried no parseable verdict object: bullet not scored";
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/** Exported for contracts tests. */
|
|
926
|
+
export function parseJudgeVerdicts(scenario: SkillEvalScenario, judge: CapturedRun): ScoredBullet[] {
|
|
927
|
+
const parsed = extractBulletsObject(judge.finalText) ?? extractBulletsObject(judge.transcript);
|
|
928
|
+
// No verdict object anywhere in the judge run. The scenario was not scored,
|
|
929
|
+
// which is a fact about the judge, so every bullet stays unmeasured rather
|
|
930
|
+
// than collecting a verdict nothing produced.
|
|
931
|
+
if (parsed === null) {
|
|
932
|
+
const reason = judgeVerdictAbsenceReason(judge);
|
|
933
|
+
return scenario.expected.map((text, i) => ({ index: i + 1, text, verdict: "unmeasured" as const, reason }));
|
|
934
|
+
}
|
|
935
|
+
const entries = new Map<number, { pass: boolean; reason: string }>();
|
|
936
|
+
if (Array.isArray(parsed.bullets)) {
|
|
937
|
+
for (const item of parsed.bullets) {
|
|
938
|
+
if (!isRecord(item)) continue;
|
|
939
|
+
const index = typeof item.index === "number" ? item.index : Number.parseInt(String(item.index), 10);
|
|
940
|
+
if (!Number.isInteger(index)) continue;
|
|
941
|
+
entries.set(index, {
|
|
942
|
+
pass: item.pass === true,
|
|
943
|
+
reason: readString(item.reason) ?? "",
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
return scenario.expected.map((text, i) => {
|
|
948
|
+
const index = i + 1;
|
|
949
|
+
const entry = entries.get(index);
|
|
950
|
+
if (entry === undefined) {
|
|
951
|
+
// The judge scored other bullets and skipped this one, so this bullet
|
|
952
|
+
// has no verdict either. Same state, narrower cause.
|
|
953
|
+
return {
|
|
954
|
+
index,
|
|
955
|
+
text,
|
|
956
|
+
verdict: "unmeasured" as const,
|
|
957
|
+
reason: "judge output omitted this bullet: bullet not scored",
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
return { index, text, verdict: entry.pass ? ("pass" as const) : ("fail" as const), reason: entry.reason };
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* Find the JSON object carrying the judge's `bullets` array anywhere in the
|
|
966
|
+
* text: models wrap verdicts in prose, code fences, or terminating tool
|
|
967
|
+
* content, so this walks candidate `{` openers around the first "bullets" key
|
|
968
|
+
* and balance-scans to the matching close brace.
|
|
969
|
+
*/
|
|
970
|
+
export function extractBulletsObject(text: string): Record<string, unknown> | null {
|
|
971
|
+
const stripped = text.replace(/```(?:json)?/g, "");
|
|
972
|
+
const bulletsAt = stripped.indexOf('"bullets"');
|
|
973
|
+
if (bulletsAt < 0) return null;
|
|
974
|
+
let start = stripped.lastIndexOf("{", bulletsAt);
|
|
975
|
+
while (start >= 0) {
|
|
976
|
+
const candidate = balancedJsonSlice(stripped, start);
|
|
977
|
+
if (candidate !== null) {
|
|
978
|
+
try {
|
|
979
|
+
const parsed = JSON.parse(candidate) as unknown;
|
|
980
|
+
if (isRecord(parsed) && Array.isArray(parsed.bullets)) return parsed;
|
|
981
|
+
} catch {
|
|
982
|
+
// Try the next opener out.
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
// lastIndexOf clamps a negative fromIndex to 0, so stepping from 0 would
|
|
986
|
+
// loop forever on a leading brace.
|
|
987
|
+
if (start === 0) break;
|
|
988
|
+
start = stripped.lastIndexOf("{", start - 1);
|
|
989
|
+
}
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/** Slice a balanced {...} region starting at `start`, string-aware. */
|
|
994
|
+
function balancedJsonSlice(text: string, start: number): string | null {
|
|
995
|
+
let depth = 0;
|
|
996
|
+
let inString = false;
|
|
997
|
+
let escaped = false;
|
|
998
|
+
for (let i = start; i < text.length; i += 1) {
|
|
999
|
+
const ch = text[i];
|
|
1000
|
+
if (escaped) {
|
|
1001
|
+
escaped = false;
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
if (ch === "\\") {
|
|
1005
|
+
if (inString) escaped = true;
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
if (ch === '"') {
|
|
1009
|
+
inString = !inString;
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
if (inString) continue;
|
|
1013
|
+
if (ch === "{") depth += 1;
|
|
1014
|
+
else if (ch === "}") {
|
|
1015
|
+
depth -= 1;
|
|
1016
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function synthesizeArtifact(
|
|
1023
|
+
skillName: string,
|
|
1024
|
+
evalsPath: string,
|
|
1025
|
+
evalsRaw: string,
|
|
1026
|
+
startedAt: string,
|
|
1027
|
+
endedAt: string,
|
|
1028
|
+
outcomes: ReadonlyArray<ScenarioOutcome>,
|
|
1029
|
+
target: string | undefined,
|
|
1030
|
+
): EvalRunArtifact {
|
|
1031
|
+
const contentHash = createHash("sha256").update(evalsRaw, "utf8").digest("hex");
|
|
1032
|
+
const stamp = startedAt.replace(/[-:.]/g, "");
|
|
1033
|
+
// Random suffix for the same reason createEvalId carries one: the stamp plus
|
|
1034
|
+
// content hash collides between two workers on the same file in one millisecond,
|
|
1035
|
+
// and the id is the artifact filename.
|
|
1036
|
+
const evalId = `skill-${skillName}-${stamp}-${contentHash.slice(0, 8)}-${randomBytes(6).toString("hex")}`;
|
|
1037
|
+
const results: EvalRunRecord[] = outcomes.map((outcome) => {
|
|
1038
|
+
const failed = outcome.bullets.some((bullet) => bullet.verdict === "fail" || bullet.verdict === "error");
|
|
1039
|
+
const unmeasured = outcome.bullets.some((bullet) => bullet.verdict === "unmeasured");
|
|
1040
|
+
const pass = outcome.bullets.length > 0 && !failed && !unmeasured;
|
|
1041
|
+
const record: EvalRunRecord = {
|
|
1042
|
+
taskId: outcome.scenario.id,
|
|
1043
|
+
runId: `${evalId}-${outcome.scenario.id}`,
|
|
1044
|
+
repeatIndex: 0,
|
|
1045
|
+
cwd: outcome.workspace,
|
|
1046
|
+
prompt: outcome.scenario.setup,
|
|
1047
|
+
tags: [
|
|
1048
|
+
"skill-eval",
|
|
1049
|
+
`skill:${skillName}`,
|
|
1050
|
+
...(unmeasured ? ["scenario:unmeasured"] : []),
|
|
1051
|
+
...outcome.bullets.map((bullet) => `bullet-${bullet.index}:${bullet.verdict}`),
|
|
1052
|
+
],
|
|
1053
|
+
pass,
|
|
1054
|
+
// 3, not 1, when the scenario carries an unscored bullet and no failed
|
|
1055
|
+
// one: a reader who sees exitCode 1 with failureClass verifier_failed
|
|
1056
|
+
// reads "this skill failed its rubric", and an unscored bullet is the
|
|
1057
|
+
// judge's silence, not the skill's answer.
|
|
1058
|
+
exitCode: pass ? 0 : failed ? 1 : 3,
|
|
1059
|
+
tokens: outcome.usage.tokens,
|
|
1060
|
+
costUsd: outcome.usage.costUsd,
|
|
1061
|
+
wallTimeMs: outcome.wallTimeMs,
|
|
1062
|
+
harness: outcome.usage.harness,
|
|
1063
|
+
commands: [],
|
|
1064
|
+
};
|
|
1065
|
+
if (failed) record.failureClass = "verifier_failed";
|
|
1066
|
+
return record;
|
|
1067
|
+
});
|
|
1068
|
+
return {
|
|
1069
|
+
version: 1,
|
|
1070
|
+
evalId,
|
|
1071
|
+
taskFile: evalsPath,
|
|
1072
|
+
taskFileHash: contentHash,
|
|
1073
|
+
clio: evalClioProvenance(),
|
|
1074
|
+
environment: evalEnvironmentProvenance(),
|
|
1075
|
+
target: target ?? null,
|
|
1076
|
+
model: null,
|
|
1077
|
+
thinking: null,
|
|
1078
|
+
paths: { taskFile: evalsPath, receipts: [], sessionLedgers: [] },
|
|
1079
|
+
repeat: 1,
|
|
1080
|
+
startedAt,
|
|
1081
|
+
endedAt,
|
|
1082
|
+
summary: summarizeEvalResults(results),
|
|
1083
|
+
results,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function sidecar(
|
|
1088
|
+
skillName: string,
|
|
1089
|
+
evalId: string,
|
|
1090
|
+
outcomes: ReadonlyArray<ScenarioOutcome>,
|
|
1091
|
+
allowNetwork: boolean,
|
|
1092
|
+
): unknown {
|
|
1093
|
+
return {
|
|
1094
|
+
version: 1,
|
|
1095
|
+
schema: "experimental",
|
|
1096
|
+
kind: "skill-eval",
|
|
1097
|
+
skill: skillName,
|
|
1098
|
+
evalId,
|
|
1099
|
+
network: networkPolicyLabel(allowNetwork),
|
|
1100
|
+
autonomy: ARM_AUTONOMY,
|
|
1101
|
+
deltas: [
|
|
1102
|
+
"bullet verdicts are judge-scored from run transcripts, not command exit codes; the evals-domain artifact carries scenario-level records with empty command lists",
|
|
1103
|
+
"a bullet the judge never scored is recorded unmeasured, not failed: its scenario record is pass:false with exitCode 3 and no failureClass",
|
|
1104
|
+
"an arm whose transcript carries the headless permission wall is recorded unmeasured with an infraError: the harness's own gate is not a verdict about the skill",
|
|
1105
|
+
"tokens and cost are rolled up from headless main-agent receipts when those receipts are present",
|
|
1106
|
+
"this sidecar is additive and is registered in overview.json files[]",
|
|
1107
|
+
],
|
|
1108
|
+
scenarios: outcomes.map((outcome) => ({
|
|
1109
|
+
id: outcome.scenario.id,
|
|
1110
|
+
title: outcome.scenario.title,
|
|
1111
|
+
setup: outcome.scenario.setup,
|
|
1112
|
+
fixtureCommands: outcome.scenario.fixtureCommands ?? null,
|
|
1113
|
+
infraError: outcome.infraError,
|
|
1114
|
+
usage: outcome.usage,
|
|
1115
|
+
bullets: outcome.bullets,
|
|
1116
|
+
baseline: sidecarRun(outcome.baseline),
|
|
1117
|
+
treatment: sidecarRun(outcome.treatment),
|
|
1118
|
+
judge: sidecarRun(outcome.judge),
|
|
1119
|
+
})),
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
function sidecarRun(run: CapturedRun | null): unknown {
|
|
1124
|
+
if (run === null) return null;
|
|
1125
|
+
return {
|
|
1126
|
+
sessionId: run.sessionId,
|
|
1127
|
+
exitCode: run.exitCode,
|
|
1128
|
+
timedOut: run.timedOut,
|
|
1129
|
+
wallTimeMs: run.wallTimeMs,
|
|
1130
|
+
transcript: run.transcript,
|
|
1131
|
+
stderrTail: run.stderr.slice(-500),
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
function printHumanReport(
|
|
1136
|
+
skillName: string,
|
|
1137
|
+
outcomes: ReadonlyArray<ScenarioOutcome>,
|
|
1138
|
+
evalId: string,
|
|
1139
|
+
evidenceId: string | null,
|
|
1140
|
+
evidenceDirectory: string | null,
|
|
1141
|
+
allowNetwork: boolean,
|
|
1142
|
+
): void {
|
|
1143
|
+
const rows: string[][] = [["scenario", "bullet", "verdict", "expected"]];
|
|
1144
|
+
for (const outcome of outcomes) {
|
|
1145
|
+
for (const bullet of outcome.bullets) {
|
|
1146
|
+
rows.push([outcome.scenario.id, String(bullet.index), bullet.verdict, truncate(bullet.text, 76)]);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
process.stdout.write(formatColumns(rows));
|
|
1150
|
+
const total = outcomes.reduce((sum, outcome) => sum + outcome.bullets.length, 0);
|
|
1151
|
+
const passed = outcomes.reduce(
|
|
1152
|
+
(sum, outcome) => sum + outcome.bullets.filter((bullet) => bullet.verdict === "pass").length,
|
|
1153
|
+
0,
|
|
1154
|
+
);
|
|
1155
|
+
const unmeasured = outcomes.reduce(
|
|
1156
|
+
(sum, outcome) => sum + outcome.bullets.filter((bullet) => bullet.verdict === "unmeasured").length,
|
|
1157
|
+
0,
|
|
1158
|
+
);
|
|
1159
|
+
// The denominator counts bullets the judge never scored, so it says how many
|
|
1160
|
+
// passed out of how many were asked, not out of how many were answered. The
|
|
1161
|
+
// unscored count is stated beside it rather than folded into the failures.
|
|
1162
|
+
const unmeasuredNote = unmeasured > 0 ? `, ${unmeasured} unmeasured` : "";
|
|
1163
|
+
process.stdout.write(
|
|
1164
|
+
`\n${skillName}: ${passed}/${total} bullets passed${unmeasuredNote} (judge-scored, experimental)\n`,
|
|
1165
|
+
);
|
|
1166
|
+
if (unmeasured > 0) {
|
|
1167
|
+
process.stdout.write(
|
|
1168
|
+
unmeasured === total
|
|
1169
|
+
? "no bullet was scored, so this run is not evidence about the skill either way\n"
|
|
1170
|
+
: `${unmeasured} of ${total} bullets were never scored; unmeasured is not a failed bullet\n`,
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
for (const outcome of outcomes) {
|
|
1174
|
+
if (outcome.infraError !== null) {
|
|
1175
|
+
process.stdout.write(`${outcome.scenario.id}: infra error: ${outcome.infraError}\n`);
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
for (const bullet of outcome.bullets.filter((item) => item.verdict !== "pass")) {
|
|
1179
|
+
process.stdout.write(
|
|
1180
|
+
`${outcome.scenario.id} bullet ${bullet.index} ${bullet.verdict}${bullet.reason.length > 0 ? `: ${bullet.reason}` : ""}\n`,
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
process.stdout.write(`${describeArmPolicyOutcome(allowNetwork)}\n`);
|
|
1185
|
+
process.stdout.write(`eval artifact: ${evalId}\n`);
|
|
1186
|
+
if (evidenceId !== null && evidenceDirectory !== null) {
|
|
1187
|
+
process.stdout.write(`evidence: ${evidenceId} at ${evidenceDirectory} (per-bullet detail in skill-eval.json)\n`);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
function preview(value: unknown): string {
|
|
1192
|
+
if (value === undefined) return "";
|
|
1193
|
+
const text = typeof value === "string" ? value : safeStringify(value);
|
|
1194
|
+
return truncate(text.replace(/\s+/g, " ").trim(), PREVIEW_MAX_CHARS);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function safeStringify(value: unknown): string {
|
|
1198
|
+
try {
|
|
1199
|
+
return JSON.stringify(value) ?? "";
|
|
1200
|
+
} catch {
|
|
1201
|
+
return String(value);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
function elide(text: string): string {
|
|
1206
|
+
if (text.length <= TRANSCRIPT_HEAD_CHARS + TRANSCRIPT_TAIL_CHARS) return text;
|
|
1207
|
+
return `${text.slice(0, TRANSCRIPT_HEAD_CHARS)}\n[... transcript elided ...]\n${text.slice(-TRANSCRIPT_TAIL_CHARS)}`;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function truncate(text: string, maxChars: number): string {
|
|
1211
|
+
if (text.length <= maxChars) return text;
|
|
1212
|
+
return `${text.slice(0, Math.max(0, maxChars - 3))}...`;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
function readString(value: unknown): string | null {
|
|
1216
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function readNumber(value: unknown): number {
|
|
1220
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
1224
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1225
|
+
}
|