@iowarp/clio-coder 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/README.md +13 -4
- package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
- package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
- package/dist/builtins-AD25UL3C.js +17 -0
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
- package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
- package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
- package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
- package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
- package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
- package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
- package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
- package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/chunk-774ILSRL.js +172 -0
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
- package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
- package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
- package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
- package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
- package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
- package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
- package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
- package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
- package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
- package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
- package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
- package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
- package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
- package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
- package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/chunk-JEQQR47K.js +3025 -0
- package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
- package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
- package/dist/chunk-MLOK6ZOS.js +2888 -0
- package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
- package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
- package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
- package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
- package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
- package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
- package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
- package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
- package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
- package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
- package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
- package/dist/chunk-VO2LKSTM.js +165 -0
- package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +32 -32
- package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
- package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
- package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
- package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
- package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
- package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
- package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
- package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
- package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
- package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/eval-IJ5VEZDJ.js +4483 -0
- package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
- package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
- package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
- package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
- package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
- package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
- package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
- package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
- package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
- package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
- package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
- package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
- package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
- package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
- package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
- package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
- package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
- package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/support-I7LOJLIF.js +38 -0
- package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
- package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
- package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
- package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
- package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
- package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
- package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
- package/dist/worker/entry.js +69 -66
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +11 -5
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +2 -2
- package/docs/configuration-and-targets.md +37 -5
- package/docs/context-engine.md +63 -4
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +2 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +77 -12
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +34 -9
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +54 -3
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +22 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +60 -41
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +87 -0
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +2 -2
- package/src/cli/agents.ts +1 -1
- package/src/cli/argv.ts +5 -0
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +7 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/evidence.ts +30 -25
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet-preflight.ts +2 -12
- package/src/cli/fleet.ts +32 -3
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +45 -11
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +54 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +18 -0
- package/src/core/defaults.ts +36 -6
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/path-boundary.ts +100 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +30 -12
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +128 -17
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +26 -1
- package/src/domains/dispatch/delegation-plan.ts +2 -5
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/execution-role.ts +9 -1
- package/src/domains/dispatch/extension.ts +309 -96
- package/src/domains/dispatch/fleet-run.ts +78 -4
- package/src/domains/dispatch/gate-role-prompts.ts +38 -0
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +6 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +84 -8
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +2 -1
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/types.ts +14 -7
- package/src/domains/dispatch/validation.ts +6 -3
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
- package/src/domains/dispatch/write-boundary.ts +262 -22
- package/src/domains/eval/artifacts/store.ts +62 -0
- package/src/domains/eval/compare/behavioral.ts +224 -0
- package/src/domains/eval/compare/compare.ts +355 -2
- package/src/domains/eval/compare/envelope.ts +128 -0
- package/src/domains/eval/compare/gates.ts +24 -6
- package/src/domains/eval/compare/thresholds.ts +30 -3
- package/src/domains/eval/execution-provenance.ts +240 -0
- package/src/domains/eval/metrics/aggregate.ts +136 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/metrics/tracked.ts +413 -0
- package/src/domains/eval/provenance.ts +117 -0
- package/src/domains/eval/reports/comparison.ts +128 -0
- package/src/domains/eval/reports/junit.ts +17 -3
- package/src/domains/eval/reports/markdown.ts +3 -3
- package/src/domains/eval/reports/text.ts +14 -0
- package/src/domains/eval/run-compare.ts +20 -0
- package/src/domains/eval/runners/clio-run.ts +139 -2
- package/src/domains/eval/runners/external-command.ts +28 -3
- package/src/domains/eval/schema/adapter.ts +111 -0
- package/src/domains/eval/schema/artifact.ts +20 -0
- package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
- package/src/domains/eval/schema/behavioral.ts +520 -0
- package/src/domains/eval/schema/execution-envelope.ts +194 -0
- package/src/domains/eval/schema/serving.ts +74 -0
- package/src/domains/eval/schema/suite.ts +38 -8
- package/src/domains/eval/schema/validate.ts +58 -3
- package/src/domains/eval/schema/verdict.ts +237 -0
- package/src/domains/eval/suites/resolve.ts +2 -0
- package/src/domains/eval/suites/run.ts +264 -33
- package/src/domains/eval/verifiers/command.ts +2 -1
- package/src/domains/eval/workspaces/temp-copy.ts +145 -13
- package/src/domains/evidence/build.ts +68 -21
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +145 -17
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +126 -4
- package/src/domains/extensions/resources.ts +21 -9
- package/src/domains/extensions/state.ts +18 -5
- package/src/domains/extensions/types.ts +6 -1
- package/src/domains/lifecycle/doctor.ts +209 -2
- package/src/domains/memory/index.ts +14 -0
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +77 -8
- package/src/domains/memory/task-memory-spend.ts +131 -0
- package/src/domains/memory/task-memory-status.ts +7 -0
- package/src/domains/memory/task-memory-telemetry.ts +2 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +69 -5
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/index.ts +7 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +192 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/prompts/contract.ts +3 -5
- package/src/domains/providers/endpoint-capacity.ts +96 -0
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/providers/index.ts +10 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
- package/src/domains/providers/runtime-resolution.ts +8 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/prompts/loader.ts +184 -17
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +96 -2
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/context-accounting.ts +52 -1
- package/src/domains/session/context-ledger.ts +37 -13
- package/src/domains/session/index.ts +6 -0
- package/src/domains/session/prompt-cache.ts +140 -0
- package/src/domains/session/prompt-manifest.ts +42 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/provider-payload.ts +29 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/entry/orchestrator.ts +176 -30
- package/src/interactive/chat-loop-messages.ts +26 -7
- package/src/interactive/chat-loop.ts +318 -41
- package/src/interactive/chat-panel.ts +62 -8
- package/src/interactive/clio-editor.ts +45 -8
- package/src/interactive/context-activity.ts +5 -1
- package/src/interactive/context-meter.ts +1 -1
- package/src/interactive/context-overlay.ts +40 -10
- package/src/interactive/cost-overlay.ts +64 -6
- package/src/interactive/dispatch-board.ts +127 -5
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +24 -1
- package/src/interactive/interactive-event-projection.ts +14 -0
- package/src/interactive/interactive-input-runtime.ts +8 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +27 -4
- package/src/interactive/memory-overlay.ts +8 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/overlay-general-openers.ts +16 -0
- package/src/interactive/overlay-key-routing.ts +38 -0
- package/src/interactive/overlay-lifecycle.ts +38 -5
- package/src/interactive/overlay-permission-lifecycle.ts +22 -2
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlays/ask-user.ts +91 -19
- package/src/interactive/overlays/help-reference.ts +4 -0
- package/src/interactive/overlays/prompts.ts +11 -1
- package/src/interactive/overlays/settings.ts +176 -48
- package/src/interactive/permission-hint.ts +34 -2
- package/src/interactive/permission-overlay.ts +159 -9
- package/src/interactive/prewarm.ts +197 -0
- package/src/interactive/render-trace.ts +162 -15
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/slash-commands.ts +7 -2
- package/src/interactive/status/controller.ts +11 -0
- package/src/interactive/status/state-machine.ts +54 -2
- package/src/interactive/status/types.ts +7 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/turn-context.ts +299 -31
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +43 -6
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-stream.ts +8 -0
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-admission.ts +12 -13
- package/src/tools/dispatch-arguments.ts +27 -0
- package/src/tools/dispatch-plan.ts +46 -9
- package/src/tools/dispatch-runner.ts +48 -13
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/monitor.ts +13 -0
- package/src/tools/registry.ts +16 -0
- package/src/tools/worker-evidence.ts +19 -13
- package/src/worker/spec-contract.ts +2 -1
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/chunk-ZGH7FGS5.js +0 -1079
- package/dist/doctor-RN4YKO2X.js +0 -87
- package/dist/eval-RUBJVSNQ.js +0 -2557
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { readSettings } from "../../core/config.js";
|
|
2
3
|
import { readClioVersion, resolvePackageRoot } from "../../core/package-root.js";
|
|
4
|
+
import type { RunReceipt } from "../dispatch/types.js";
|
|
5
|
+
import type { EvalServingConfigurationV1 } from "./schema/serving.js";
|
|
6
|
+
import type { EvalSuiteTargetV2 } from "./schema/suite.js";
|
|
3
7
|
import type { EvalClioProvenance, EvalEnvironmentProvenance } from "./types.js";
|
|
4
8
|
|
|
5
9
|
export interface EvalProvenanceOptions {
|
|
@@ -7,6 +11,14 @@ export interface EvalProvenanceOptions {
|
|
|
7
11
|
commit?: string | null;
|
|
8
12
|
}
|
|
9
13
|
|
|
14
|
+
export interface EvalServingObservation {
|
|
15
|
+
targetId: string;
|
|
16
|
+
runtimeId: string | null;
|
|
17
|
+
modelId: string | null;
|
|
18
|
+
thinkingLevel: string | null;
|
|
19
|
+
compiledPromptHash: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
export function evalClioProvenance(options: EvalProvenanceOptions = {}): EvalClioProvenance {
|
|
11
23
|
return {
|
|
12
24
|
version: readClioVersion(),
|
|
@@ -22,6 +34,45 @@ export function evalEnvironmentProvenance(): EvalEnvironmentProvenance {
|
|
|
22
34
|
};
|
|
23
35
|
}
|
|
24
36
|
|
|
37
|
+
/** Capture serving identity after the matrix has run while a local server is awake. */
|
|
38
|
+
export async function evalServingConfiguration(
|
|
39
|
+
targets: ReadonlyArray<EvalSuiteTargetV2>,
|
|
40
|
+
observations: ReadonlyArray<EvalServingObservation>,
|
|
41
|
+
): Promise<EvalServingConfigurationV1> {
|
|
42
|
+
const targetId = targets.length === 1 ? (targets[0]?.id ?? "unknown") : "multiple";
|
|
43
|
+
const configured = configuredTarget(targetId);
|
|
44
|
+
const target = targets.length === 1 ? targets[0] : undefined;
|
|
45
|
+
const runtimeId = consensus(observations.map((entry) => entry.runtimeId)) ?? configured?.runtime ?? null;
|
|
46
|
+
const modelId =
|
|
47
|
+
consensus(observations.map((entry) => entry.modelId)) ?? target?.model ?? configured?.defaultModel ?? null;
|
|
48
|
+
const props = configured?.url === undefined ? null : await readServingProps(configured.url, modelId);
|
|
49
|
+
return {
|
|
50
|
+
targetId,
|
|
51
|
+
runtimeId,
|
|
52
|
+
modelId,
|
|
53
|
+
serverBuild: props?.serverBuild ?? null,
|
|
54
|
+
total_slots: props?.totalSlots ?? null,
|
|
55
|
+
thinkingLevel: consensus(observations.map((entry) => entry.thinkingLevel)) ?? target?.thinking ?? null,
|
|
56
|
+
compiledPromptHash: consensus(observations.map((entry) => entry.compiledPromptHash)),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function evalServingObservationFrom(
|
|
61
|
+
target: EvalSuiteTargetV2,
|
|
62
|
+
receipt: RunReceipt | null,
|
|
63
|
+
compiledPromptHashes: ReadonlyArray<string>,
|
|
64
|
+
): EvalServingObservation {
|
|
65
|
+
const compiledPromptHash =
|
|
66
|
+
receipt?.staticCompositionHash ?? receipt?.compiledPromptHash ?? consensus(compiledPromptHashes);
|
|
67
|
+
return {
|
|
68
|
+
targetId: receipt?.targetId ?? target.id,
|
|
69
|
+
runtimeId: receipt?.runtimeId ?? null,
|
|
70
|
+
modelId: receipt?.wireModelId ?? target.model ?? null,
|
|
71
|
+
thinkingLevel: receipt?.runtimeResolution?.effectiveThinkingLevel ?? target.thinking ?? null,
|
|
72
|
+
compiledPromptHash,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
25
76
|
function currentClioCommit(): string | null {
|
|
26
77
|
const result = spawnSync("git", ["rev-parse", "HEAD"], {
|
|
27
78
|
cwd: resolvePackageRoot(),
|
|
@@ -33,3 +84,69 @@ function currentClioCommit(): string | null {
|
|
|
33
84
|
const value = result.stdout.trim();
|
|
34
85
|
return value.length > 0 ? value : null;
|
|
35
86
|
}
|
|
87
|
+
|
|
88
|
+
interface ConfiguredServingTarget {
|
|
89
|
+
runtime: string;
|
|
90
|
+
url?: string;
|
|
91
|
+
defaultModel?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function configuredTarget(targetId: string): ConfiguredServingTarget | null {
|
|
95
|
+
try {
|
|
96
|
+
const target = readSettings().targets.find((entry) => entry.id === targetId);
|
|
97
|
+
if (target === undefined) return null;
|
|
98
|
+
return {
|
|
99
|
+
runtime: target.runtime,
|
|
100
|
+
...(target.url === undefined ? {} : { url: target.url }),
|
|
101
|
+
...(target.defaultModel === undefined ? {} : { defaultModel: target.defaultModel }),
|
|
102
|
+
};
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function readServingProps(
|
|
109
|
+
targetUrl: string,
|
|
110
|
+
modelId: string | null,
|
|
111
|
+
): Promise<{ serverBuild: string | null; totalSlots: number | null } | null> {
|
|
112
|
+
const root = targetUrl.replace(/\/+$/u, "").replace(/\/v1$/u, "");
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch(`${root}/props`, { signal: AbortSignal.timeout(5_000) });
|
|
115
|
+
if (!response.ok) return null;
|
|
116
|
+
const value: unknown = await response.json();
|
|
117
|
+
if (!isRecord(value)) return null;
|
|
118
|
+
const totalSlots = positiveInteger(value.total_slots) ?? (await readServingSlots(root, modelId));
|
|
119
|
+
return {
|
|
120
|
+
serverBuild: typeof value.build_info === "string" && value.build_info.length > 0 ? value.build_info : null,
|
|
121
|
+
totalSlots,
|
|
122
|
+
};
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function readServingSlots(root: string, modelId: string | null): Promise<number | null> {
|
|
129
|
+
if (modelId === null) return null;
|
|
130
|
+
try {
|
|
131
|
+
const query = new URLSearchParams({ model: modelId });
|
|
132
|
+
const response = await fetch(`${root}/slots?${query}`, { signal: AbortSignal.timeout(5_000) });
|
|
133
|
+
if (!response.ok) return null;
|
|
134
|
+
const value: unknown = await response.json();
|
|
135
|
+
return Array.isArray(value) && value.length > 0 ? value.length : null;
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function consensus(values: ReadonlyArray<string | null>): string | null {
|
|
142
|
+
const known = [...new Set(values.filter((value): value is string => typeof value === "string" && value.length > 0))];
|
|
143
|
+
return known.length === 1 ? (known[0] ?? null) : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function positiveInteger(value: unknown): number | null {
|
|
147
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
151
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
152
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { EvalCompareV4Summary } from "../compare/compare.js";
|
|
2
|
+
import { renderEvalComparisonV4 } from "../compare/compare.js";
|
|
3
|
+
|
|
4
|
+
export type EvalComparisonReportFormat = "text" | "json" | "md" | "junit";
|
|
5
|
+
|
|
6
|
+
export function renderEvalComparisonReportV1(
|
|
7
|
+
summary: EvalCompareV4Summary,
|
|
8
|
+
format: EvalComparisonReportFormat,
|
|
9
|
+
): string {
|
|
10
|
+
if (format === "json") return `${JSON.stringify(summary, null, 2)}\n`;
|
|
11
|
+
if (format === "md") return renderMarkdown(summary);
|
|
12
|
+
if (format === "junit") return renderJunit(summary);
|
|
13
|
+
return renderEvalComparisonV4(summary);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function renderMarkdown(summary: EvalCompareV4Summary): string {
|
|
17
|
+
const rows = summary.behavioralMetrics.map(
|
|
18
|
+
(row) =>
|
|
19
|
+
`| ${cell(row.scenarioId)} | ${cell(row.role)} | ${cell(`${row.target.id}/${row.target.model ?? "none"}`)} | ${row.family} | ${row.metric} | ${format(row.baseline.mean)} | ${format(row.baseline.variance)} | ${row.baseline.measured}/${row.baseline.observations} | ${format(row.candidate.mean)} | ${format(row.candidate.variance)} | ${row.candidate.measured}/${row.candidate.observations} | ${row.change} | ${row.varianceChange} | ${row.comparability.comparable ? "comparable" : cell(row.comparability.mismatchedFields.join(", "))} | ${row.hardGate ? "hard" : "informational"} |`,
|
|
20
|
+
);
|
|
21
|
+
const scenarioRows = summary.scenarioReports.map(
|
|
22
|
+
(report) => `| ${cell(report.id)} | ${changeCounts(report.metrics)} | ${changeCounts(report.variance)} |`,
|
|
23
|
+
);
|
|
24
|
+
const roleRows = summary.roleReports.map(
|
|
25
|
+
(report) => `| ${cell(report.id)} | ${changeCounts(report.metrics)} | ${changeCounts(report.variance)} |`,
|
|
26
|
+
);
|
|
27
|
+
return [
|
|
28
|
+
`# Eval comparison ${summary.baselineEvalId} → ${summary.candidateEvalId}`,
|
|
29
|
+
"",
|
|
30
|
+
`Behavioral hard gate: **${summary.hardGate.pass ? "pass" : "fail"}**`,
|
|
31
|
+
...summary.hardGate.failures.map(
|
|
32
|
+
(failure) =>
|
|
33
|
+
`- Hard failure: ${failure.scenarioId} / ${failure.role} / ${failure.target.id}/${failure.target.model ?? "none"} / ${failure.metric}: ${failure.change}`,
|
|
34
|
+
),
|
|
35
|
+
...summary.envelopeMismatches.map(
|
|
36
|
+
(mismatch) =>
|
|
37
|
+
`- Incomparable envelope: ${mismatch.scenarioId} / ${mismatch.role} / ${mismatch.target.id}/${mismatch.target.model ?? "none"}: ${mismatch.fields.join(", ")}`,
|
|
38
|
+
),
|
|
39
|
+
...summary.affectedCorpusResults.map(
|
|
40
|
+
(result) => `- Affected corpus result: ${result.scenarioId} / ${result.role}: ${result.changedFields.join(", ")}`,
|
|
41
|
+
),
|
|
42
|
+
`Pass-rate delta: ${(summary.passRateDelta * 100).toFixed(2)}%`,
|
|
43
|
+
`Token delta: ${summary.tokenDelta === null ? "unmeasured" : summary.tokenDelta}`,
|
|
44
|
+
`Wall-time delta ms: ${summary.wallTimeDelta}`,
|
|
45
|
+
"",
|
|
46
|
+
"| Scenario | Role | Target/model | Family | Metric | Baseline mean | Baseline variance | Baseline measured | Candidate mean | Candidate variance | Candidate measured | Change | Variance | Comparability | Gate |",
|
|
47
|
+
"|---|---|---|---|---|---:|---:|---:|---:|---:|---:|---|---|---|---|",
|
|
48
|
+
...rows,
|
|
49
|
+
"",
|
|
50
|
+
"## Per-scenario baseline/candidate report",
|
|
51
|
+
"",
|
|
52
|
+
"| Scenario | Metric changes | Variance changes |",
|
|
53
|
+
"|---|---|---|",
|
|
54
|
+
...scenarioRows,
|
|
55
|
+
"",
|
|
56
|
+
"## Per-role baseline/candidate report",
|
|
57
|
+
"",
|
|
58
|
+
"| Role | Metric changes | Variance changes |",
|
|
59
|
+
"|---|---|---|",
|
|
60
|
+
...roleRows,
|
|
61
|
+
"",
|
|
62
|
+
].join("\n");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function renderJunit(summary: EvalCompareV4Summary): string {
|
|
66
|
+
const failures = new Set(
|
|
67
|
+
summary.hardGate.failures.map((failure) =>
|
|
68
|
+
JSON.stringify([failure.scenarioId, failure.role, failure.target.id, failure.target.model, failure.metric]),
|
|
69
|
+
),
|
|
70
|
+
);
|
|
71
|
+
const represented = new Set<string>();
|
|
72
|
+
const cases = summary.behavioralMetrics.map((row) => {
|
|
73
|
+
const name = `${row.scenarioId}[${row.role}:${row.target.id}:${row.target.model ?? "none"}].${row.metric}`;
|
|
74
|
+
const key = JSON.stringify([row.scenarioId, row.role, row.target.id, row.target.model, row.metric]);
|
|
75
|
+
represented.add(key);
|
|
76
|
+
const detail = `change=${row.change} variance=${row.varianceChange} baseline=${format(row.baseline.mean)} candidate=${format(row.candidate.mean)}`;
|
|
77
|
+
return failures.has(key)
|
|
78
|
+
? ` <testcase classname="eval.behavior.${escapeXml(row.family)}" name="${escapeXml(name)}"><failure message="${escapeXml(row.change)}">${escapeXml(detail)}</failure></testcase>`
|
|
79
|
+
: ` <testcase classname="eval.behavior.${escapeXml(row.family)}" name="${escapeXml(name)}"><system-out>${escapeXml(detail)}</system-out></testcase>`;
|
|
80
|
+
});
|
|
81
|
+
for (const failure of summary.hardGate.failures) {
|
|
82
|
+
const key = JSON.stringify([
|
|
83
|
+
failure.scenarioId,
|
|
84
|
+
failure.role,
|
|
85
|
+
failure.target.id,
|
|
86
|
+
failure.target.model,
|
|
87
|
+
failure.metric,
|
|
88
|
+
]);
|
|
89
|
+
if (represented.has(key)) continue;
|
|
90
|
+
const name = `${failure.scenarioId}[${failure.role}:${failure.target.id}:${failure.target.model ?? "none"}].${failure.metric}`;
|
|
91
|
+
cases.push(
|
|
92
|
+
` <testcase classname="eval.behavior.hard" name="${escapeXml(name)}"><failure message="${escapeXml(failure.change)}">hard behavioral gate</failure></testcase>`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
for (const failure of summary.hardGate.envelopeFailures) {
|
|
96
|
+
const name = `${failure.scenarioId}[${failure.role}:${failure.target.id}:${failure.target.model ?? "none"}].execution-envelope`;
|
|
97
|
+
cases.push(
|
|
98
|
+
` <testcase classname="eval.behavior.envelope" name="${escapeXml(name)}"><failure message="incomparable">${escapeXml(failure.fields.join(", "))}</failure></testcase>`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return [
|
|
102
|
+
`<testsuite name="eval-comparison" tests="${cases.length}" failures="${summary.hardGate.failures.length + summary.hardGate.envelopeFailures.length}">`,
|
|
103
|
+
...cases,
|
|
104
|
+
"</testsuite>",
|
|
105
|
+
"",
|
|
106
|
+
].join("\n");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function changeCounts(counts: {
|
|
110
|
+
improved: number;
|
|
111
|
+
regressed: number;
|
|
112
|
+
unchanged: number;
|
|
113
|
+
incomparable: number;
|
|
114
|
+
}): string {
|
|
115
|
+
return `improved ${counts.improved}, regressed ${counts.regressed}, unchanged ${counts.unchanged}, incomparable ${counts.incomparable}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function format(value: number | null): string {
|
|
119
|
+
return value === null ? "null" : Number.isInteger(value) ? String(value) : value.toFixed(4);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function cell(value: string): string {
|
|
123
|
+
return value.replaceAll("|", "\\|");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function escapeXml(value: string): string {
|
|
127
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
128
|
+
}
|
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import type { EvalArtifactV4 } from "../schema/artifact.js";
|
|
2
2
|
|
|
3
3
|
export function renderEvalJunitReportV4(artifact: EvalArtifactV4): string {
|
|
4
|
+
let failures = 0;
|
|
5
|
+
let skipped = 0;
|
|
4
6
|
const cases = artifact.results
|
|
5
7
|
.map((result) => {
|
|
6
8
|
const name = escapeXml(
|
|
7
9
|
`${result.taskId}[${result.target.id}:${result.target.model ?? "default"}:${result.repeatIndex}]`,
|
|
8
10
|
);
|
|
9
|
-
if (result.pass)
|
|
10
|
-
|
|
11
|
+
if (!result.pass) {
|
|
12
|
+
failures += 1;
|
|
13
|
+
return ` <testcase name="${name}"><failure message="${escapeXml(result.failureClass ?? "failed")}" /></testcase>`;
|
|
14
|
+
}
|
|
15
|
+
const outcome = result.behavioral?.outcome;
|
|
16
|
+
if (outcome === "behavioral_failure" || outcome === "infrastructure_failure") {
|
|
17
|
+
failures += 1;
|
|
18
|
+
return ` <testcase name="${name}"><failure message="${escapeXml(outcome)}" /></testcase>`;
|
|
19
|
+
}
|
|
20
|
+
if (outcome === "unknown" || outcome === "unmeasured") {
|
|
21
|
+
skipped += 1;
|
|
22
|
+
return ` <testcase name="${name}"><skipped message="behavioral ${escapeXml(outcome)}" /></testcase>`;
|
|
23
|
+
}
|
|
24
|
+
return ` <testcase name="${name}" />`;
|
|
11
25
|
})
|
|
12
26
|
.join("\n");
|
|
13
27
|
return [
|
|
14
|
-
`<testsuite name="${escapeXml(artifact.suite.id)}" tests="${artifact.summary.runs}" failures="${
|
|
28
|
+
`<testsuite name="${escapeXml(artifact.suite.id)}" tests="${artifact.summary.runs}" failures="${failures}" skipped="${skipped}">`,
|
|
15
29
|
cases,
|
|
16
30
|
"</testsuite>",
|
|
17
31
|
"",
|
|
@@ -8,11 +8,11 @@ export function renderEvalMarkdownReportV4(artifact: EvalArtifactV4): string {
|
|
|
8
8
|
`Target: ${artifact.matrix.target}`,
|
|
9
9
|
`Pass rate: ${(artifact.summary.passRate * 100).toFixed(2)}%`,
|
|
10
10
|
"",
|
|
11
|
-
"| Task | Target | Model | Repeat |
|
|
12
|
-
"
|
|
11
|
+
"| Task | Role | Target | Model | Repeat | Result | Behavioral | Failure |",
|
|
12
|
+
"|---|---|---|---|---:|---|---|---|",
|
|
13
13
|
...artifact.results.map(
|
|
14
14
|
(result) =>
|
|
15
|
-
`| ${result.taskId} | ${result.target.id} | ${result.target.model ?? ""} | ${result.repeatIndex} | ${result.pass ? "pass" : "fail"} | ${result.failureClass ?? ""} |`,
|
|
15
|
+
`| ${result.taskId} | ${result.behavioralMetrics?.role ?? ""} | ${result.target.id} | ${result.target.model ?? ""} | ${result.repeatIndex} | ${result.pass ? "pass" : "fail"} | ${result.behavioral?.outcome ?? "unmeasured"} | ${result.failureClass ?? ""} |`,
|
|
16
16
|
),
|
|
17
17
|
"",
|
|
18
18
|
];
|
|
@@ -2,6 +2,15 @@ import type { EvalArtifactV4 } from "../schema/artifact.js";
|
|
|
2
2
|
|
|
3
3
|
export function renderEvalTextReportV4(artifact: EvalArtifactV4): string {
|
|
4
4
|
const tokens = artifact.summary.tokens;
|
|
5
|
+
const behavioral = artifact.results.flatMap((result) =>
|
|
6
|
+
result.behavioral === undefined ? [] : [result.behavioral.outcome],
|
|
7
|
+
);
|
|
8
|
+
const behavioralSummary =
|
|
9
|
+
behavioral.length === 0
|
|
10
|
+
? []
|
|
11
|
+
: [
|
|
12
|
+
`behavioral: pass=${count(behavioral, "pass")} failure=${count(behavioral, "behavioral_failure")} unknown=${count(behavioral, "unknown")} unmeasured=${count(behavioral, "unmeasured")} infrastructure=${count(behavioral, "infrastructure_failure")}`,
|
|
13
|
+
];
|
|
5
14
|
return [
|
|
6
15
|
`eval: ${artifact.evalId}`,
|
|
7
16
|
`suite: ${artifact.suite.id}`,
|
|
@@ -20,6 +29,11 @@ export function renderEvalTextReportV4(artifact: EvalArtifactV4): string {
|
|
|
20
29
|
? `tokens total: ${tokens.total}`
|
|
21
30
|
: `tokens total: ${tokens.total} (measured in ${tokens.measuredRuns} of ${tokens.runs} runs)`,
|
|
22
31
|
`wall time ms: ${artifact.summary.wallTimeMs}`,
|
|
32
|
+
...behavioralSummary,
|
|
23
33
|
"",
|
|
24
34
|
].join("\n");
|
|
25
35
|
}
|
|
36
|
+
|
|
37
|
+
function count(values: ReadonlyArray<string>, wanted: string): number {
|
|
38
|
+
return values.filter((value) => value === wanted).length;
|
|
39
|
+
}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import { subtractEvalHarnessMetrics } from "./harness-metrics.js";
|
|
2
|
+
import type { EvalMetricSource } from "./schema/verdict.js";
|
|
2
3
|
import type { EvalFailureClass, EvalHarnessMetrics, EvalRunArtifact, EvalRunRecord, EvalSummary } from "./types.js";
|
|
3
4
|
|
|
4
5
|
export const EVAL_COMPARE_MATCHING_RULE = "taskId+repeatIndex";
|
|
5
6
|
|
|
7
|
+
export class EvalTrackedMetricSourceMismatchError extends Error {
|
|
8
|
+
constructor(metric: string, baseline: ReadonlyArray<EvalMetricSource>, candidate: ReadonlyArray<EvalMetricSource>) {
|
|
9
|
+
super(
|
|
10
|
+
`tracked metric ${metric} cannot compare estimated and measured values (baseline=${baseline.join(",") || "none"}, candidate=${candidate.join(",") || "none"})`,
|
|
11
|
+
);
|
|
12
|
+
this.name = "EvalTrackedMetricSourceMismatchError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Refuse a delta when exactly one side contains estimated observations. */
|
|
17
|
+
export function assertComparableTrackedMetricSources(
|
|
18
|
+
metric: string,
|
|
19
|
+
baseline: ReadonlyArray<EvalMetricSource>,
|
|
20
|
+
candidate: ReadonlyArray<EvalMetricSource>,
|
|
21
|
+
): void {
|
|
22
|
+
if (baseline.includes("estimated") === candidate.includes("estimated")) return;
|
|
23
|
+
throw new EvalTrackedMetricSourceMismatchError(metric, baseline, candidate);
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
export interface EvalCompareTotals {
|
|
7
27
|
passed: number;
|
|
8
28
|
failed: number;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
1
2
|
import { shellQuote } from "../../../core/shell-quote.js";
|
|
2
|
-
import {
|
|
3
|
+
import { clioStateDir } from "../../../core/xdg.js";
|
|
4
|
+
import {
|
|
5
|
+
evidenceMetricsFromReceipt,
|
|
6
|
+
readRunEnvelopeForReceipt,
|
|
7
|
+
receiptFromRunJsonStdout,
|
|
8
|
+
} from "../metrics/evidence.js";
|
|
3
9
|
import { streamInvariantMetrics } from "../metrics/invariants.js";
|
|
4
10
|
import { tokenMetricEntries } from "../metrics/token-stream.js";
|
|
5
11
|
import type { EvalRunnerV2, EvalSuiteTargetV2 } from "../schema/suite.js";
|
|
@@ -12,6 +18,7 @@ export async function runClioRunRunner(
|
|
|
12
18
|
timeoutMs: number,
|
|
13
19
|
target: EvalSuiteTargetV2,
|
|
14
20
|
env?: NodeJS.ProcessEnv,
|
|
21
|
+
readObservation?: { allowedPaths: string[]; decoyPaths: string[] },
|
|
15
22
|
): Promise<EvalRunnerOutput> {
|
|
16
23
|
const prompt = runner.prompt ?? "";
|
|
17
24
|
const args = [
|
|
@@ -23,6 +30,7 @@ export async function runClioRunRunner(
|
|
|
23
30
|
shellQuote(target.id),
|
|
24
31
|
...(target.model === undefined ? [] : ["--model", shellQuote(target.model)]),
|
|
25
32
|
...(target.thinking === undefined ? [] : ["--thinking", shellQuote(target.thinking)]),
|
|
33
|
+
...(runner.autonomy === undefined ? [] : ["--autonomy", runner.autonomy]),
|
|
26
34
|
shellQuote(prompt),
|
|
27
35
|
];
|
|
28
36
|
const result = await runShellCommand(`${process.execPath} ${args.join(" ")}`, cwd, runner.timeoutMs ?? timeoutMs, env);
|
|
@@ -31,10 +39,16 @@ export async function runClioRunRunner(
|
|
|
31
39
|
const tokens = result.usage;
|
|
32
40
|
const toolMetricStream = result.metricJsonl.length > 0 ? result.metricJsonl : result.stdout;
|
|
33
41
|
const tools = toolCallMetricsFromJsonl(toolMetricStream);
|
|
42
|
+
const behavioralTools = toolBehaviorMetricEntriesFromJsonl(toolMetricStream, cwd, readObservation);
|
|
34
43
|
// Evidence metrics resolve only from the sealed receipt the --agent path
|
|
35
44
|
// prints; a runner without a receipt leaves them absent so any gate on
|
|
36
45
|
// them fails closed instead of reading prose labels.
|
|
37
46
|
const receipt = receiptFromRunJsonStdout(result.stdout);
|
|
47
|
+
// The canonical trust metrics authenticate the receipt against the ledger
|
|
48
|
+
// row the child wrote; a stdout receipt on its own only ever reads as an
|
|
49
|
+
// unchecked seal.
|
|
50
|
+
const envelope =
|
|
51
|
+
receipt === null ? null : readRunEnvelopeForReceipt(receipt, env?.CLIO_CODER_STATE_DIR ?? clioStateDir());
|
|
38
52
|
return {
|
|
39
53
|
assignmentId: receipt === null ? null : (receipt.lineage?.rootRunId ?? receipt.runId),
|
|
40
54
|
terminalReceiptDigest: receipt?.integrity.digest ?? null,
|
|
@@ -51,8 +65,9 @@ export async function runClioRunRunner(
|
|
|
51
65
|
"tools.totalCalls": tools.totalCalls,
|
|
52
66
|
"tools.failed": tools.failed,
|
|
53
67
|
"tools.blocked": tools.blocked,
|
|
68
|
+
...behavioralTools,
|
|
54
69
|
"verifier.exitCode": result.exitCode,
|
|
55
|
-
...(receipt === null ? {} : evidenceMetricsFromReceipt(receipt)),
|
|
70
|
+
...(receipt === null ? {} : evidenceMetricsFromReceipt(receipt, { envelope })),
|
|
56
71
|
...(receipt === null
|
|
57
72
|
? {}
|
|
58
73
|
: { "evidence.qualityLabel": receipt.quality.typedValidations.length > 0 ? "measured" : "unmeasured" }),
|
|
@@ -60,8 +75,11 @@ export async function runClioRunRunner(
|
|
|
60
75
|
artifacts: {
|
|
61
76
|
stdout: result.stdout,
|
|
62
77
|
stderr: result.stderr,
|
|
78
|
+
callLedger: JSON.stringify(result.ledgerEntries),
|
|
63
79
|
...(receipt === null ? {} : { receipt: JSON.stringify(receipt) }),
|
|
64
80
|
},
|
|
81
|
+
receipt,
|
|
82
|
+
ledgerEntries: result.ledgerEntries,
|
|
65
83
|
};
|
|
66
84
|
}
|
|
67
85
|
|
|
@@ -118,6 +136,125 @@ export function toolCallMetricsFromJsonl(stdout: string): ToolCallMetrics {
|
|
|
118
136
|
return canonicalFinishes.totalCalls > 0 ? canonicalFinishes : executionEnds;
|
|
119
137
|
}
|
|
120
138
|
|
|
139
|
+
interface BehavioralToolTerminal {
|
|
140
|
+
callId: string | null;
|
|
141
|
+
tool: string;
|
|
142
|
+
outcome: ToolOutcome;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Reduce the same terminal stream to bounded behavioral facts. Tool names are
|
|
147
|
+
* a dynamic metric suffix because extensions may add tools. Read paths become
|
|
148
|
+
* bounded counters against the suite's explicit public allowlist and decoy
|
|
149
|
+
* list; they never become behavioral fact values or evidence excerpts.
|
|
150
|
+
*/
|
|
151
|
+
export function toolBehaviorMetricEntriesFromJsonl(
|
|
152
|
+
stdout: string,
|
|
153
|
+
cwd: string,
|
|
154
|
+
readObservation?: { allowedPaths: string[]; decoyPaths: string[] },
|
|
155
|
+
): Record<string, number> {
|
|
156
|
+
const starts = new Map<string, { tool: string; path: string | null }>();
|
|
157
|
+
const readPaths = new Set<string>();
|
|
158
|
+
const executionEnds: BehavioralToolTerminal[] = [];
|
|
159
|
+
const canonicalFinishes: BehavioralToolTerminal[] = [];
|
|
160
|
+
const seenExecution = new Set<string>();
|
|
161
|
+
const seenCanonical = new Set<string>();
|
|
162
|
+
|
|
163
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
164
|
+
if (line.trim().length === 0) continue;
|
|
165
|
+
let event: Record<string, unknown>;
|
|
166
|
+
try {
|
|
167
|
+
const parsed: unknown = JSON.parse(line);
|
|
168
|
+
if (!isRecord(parsed)) continue;
|
|
169
|
+
event = parsed;
|
|
170
|
+
} catch {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (event.type === "tool_execution_start") {
|
|
174
|
+
const callId = stringField(event, "toolCallId");
|
|
175
|
+
const tool = stringField(event, "toolName");
|
|
176
|
+
if (callId === undefined || tool === undefined) continue;
|
|
177
|
+
const path = tool === "read" && isRecord(event.args) ? toolPath(event.args) : null;
|
|
178
|
+
starts.set(callId, { tool, path });
|
|
179
|
+
if (path !== null) readPaths.add(normalizeObservedPath(cwd, path));
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (event.type === "tool_execution_end") {
|
|
183
|
+
const callId = stringField(event, "toolCallId") ?? null;
|
|
184
|
+
if (callId !== null && seenExecution.has(callId)) continue;
|
|
185
|
+
if (callId !== null) seenExecution.add(callId);
|
|
186
|
+
const tool = stringField(event, "toolName") ?? (callId === null ? undefined : starts.get(callId)?.tool);
|
|
187
|
+
if (tool === undefined) continue;
|
|
188
|
+
executionEnds.push({ callId, tool, outcome: toolOutcome(event) ?? (event.isError === true ? "error" : "ok") });
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (event.type !== "clio_tool_finish" || !isRecord(event.payload)) continue;
|
|
192
|
+
const outcome = toolOutcome(event.payload);
|
|
193
|
+
const tool = stringField(event.payload, "tool");
|
|
194
|
+
if (outcome === undefined || tool === undefined) continue;
|
|
195
|
+
const callId = stringField(event.payload, "toolCallId") ?? stringField(event, "toolCallId") ?? null;
|
|
196
|
+
if (callId !== null && seenCanonical.has(callId)) continue;
|
|
197
|
+
if (callId !== null) seenCanonical.add(callId);
|
|
198
|
+
canonicalFinishes.push({ callId, tool, outcome });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const terminals = canonicalFinishes.length > 0 ? canonicalFinishes : executionEnds;
|
|
202
|
+
const calls = new Map<string, number>();
|
|
203
|
+
const blocked = new Map<string, number>();
|
|
204
|
+
for (const terminal of terminals) {
|
|
205
|
+
const tool = metricToolName(terminal.tool);
|
|
206
|
+
calls.set(tool, (calls.get(tool) ?? 0) + 1);
|
|
207
|
+
if (terminal.outcome === "blocked") blocked.set(tool, (blocked.get(tool) ?? 0) + 1);
|
|
208
|
+
}
|
|
209
|
+
const namedTools = new Set(["bash", "dispatch", "read", ...calls.keys(), ...blocked.keys()]);
|
|
210
|
+
const entries: Record<string, number> = { "tools.read.distinctPaths": readPaths.size };
|
|
211
|
+
for (const tool of [...namedTools].sort()) {
|
|
212
|
+
entries[`tools.calls.${tool}`] = calls.get(tool) ?? 0;
|
|
213
|
+
entries[`tools.blocked.${tool}`] = blocked.get(tool) ?? 0;
|
|
214
|
+
}
|
|
215
|
+
if (readObservation !== undefined) {
|
|
216
|
+
const allowed = readObservation.allowedPaths.map((path) => normalizeObservedPath(cwd, path));
|
|
217
|
+
const decoys = readObservation.decoyPaths.map((path) => normalizeObservedPath(cwd, path));
|
|
218
|
+
entries["tools.read.outsideAllowed"] = [...readPaths].filter(
|
|
219
|
+
(path) => !allowed.some((root) => pathWithin(path, root)),
|
|
220
|
+
).length;
|
|
221
|
+
entries["tools.read.decoyHits"] = [...readPaths].filter((path) =>
|
|
222
|
+
decoys.some((root) => pathWithin(path, root)),
|
|
223
|
+
).length;
|
|
224
|
+
}
|
|
225
|
+
return entries;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function toolPath(args: Record<string, unknown>): string | null {
|
|
229
|
+
for (const field of ["path", "filePath", "file_path"]) {
|
|
230
|
+
const value = args[field];
|
|
231
|
+
if (typeof value === "string" && value.length > 0 && value.length <= 4_096) return value;
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function normalizeObservedPath(cwd: string, path: string): string {
|
|
237
|
+
const absolute = resolve(cwd, path);
|
|
238
|
+
const local = relative(cwd, absolute);
|
|
239
|
+
return (isAbsolute(path) && (local.startsWith("..") || isAbsolute(local)) ? absolute : local || ".")
|
|
240
|
+
.split(sep)
|
|
241
|
+
.join("/");
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function pathWithin(path: string, root: string): boolean {
|
|
245
|
+
if (root === ".") return !isAbsolute(path) && path !== ".." && !path.startsWith("../");
|
|
246
|
+
return path === root || path.startsWith(`${root}/`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function metricToolName(tool: string): string {
|
|
250
|
+
return (
|
|
251
|
+
tool
|
|
252
|
+
.toLowerCase()
|
|
253
|
+
.replaceAll(/[^a-z0-9_-]/gu, "_")
|
|
254
|
+
.slice(0, 64) || "unknown"
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
121
258
|
function recordToolOutcome(metrics: ToolCallMetrics, outcome: ToolOutcome): void {
|
|
122
259
|
metrics.totalCalls += 1;
|
|
123
260
|
if (outcome === "error") metrics.failed += 1;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { performance } from "node:perf_hooks";
|
|
3
|
+
import type { RunReceipt } from "../../dispatch/types.js";
|
|
4
|
+
import type { SessionEntry } from "../../session/entries.js";
|
|
5
|
+
import { createEvalCallLedgerFold } from "../metrics/call-ledger-stream.js";
|
|
3
6
|
import {
|
|
4
7
|
addFleetLoopObservations,
|
|
5
8
|
createFleetLoopFold,
|
|
@@ -33,6 +36,10 @@ export interface EvalRunnerOutput {
|
|
|
33
36
|
wallTimeMs: number;
|
|
34
37
|
metrics: Record<string, number | string | boolean | null>;
|
|
35
38
|
artifacts: Record<string, string | string[] | null>;
|
|
39
|
+
/** Parsed sealed receipt when this runner exposes one. */
|
|
40
|
+
receipt?: RunReceipt | null;
|
|
41
|
+
/** Structured per-call facts folded before bounded stdout truncation. */
|
|
42
|
+
ledgerEntries?: SessionEntry[];
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
export interface ShellCommandResult {
|
|
@@ -46,6 +53,7 @@ export interface ShellCommandResult {
|
|
|
46
53
|
/** Wire-stream structural invariants folded live, for the same reason. */
|
|
47
54
|
streamInvariants: EvalStreamInvariants;
|
|
48
55
|
fleetLoops: EvalFleetLoopObservation;
|
|
56
|
+
ledgerEntries: SessionEntry[];
|
|
49
57
|
stderr: string;
|
|
50
58
|
wallTimeMs: number;
|
|
51
59
|
timedOut: boolean;
|
|
@@ -75,6 +83,7 @@ export async function runExternalCommandRunner(
|
|
|
75
83
|
let usage = UNMEASURED_TOKEN_USAGE;
|
|
76
84
|
let streamInvariants = EMPTY_STREAM_INVARIANTS;
|
|
77
85
|
let fleetLoops = EMPTY_FLEET_LOOP_OBSERVATION;
|
|
86
|
+
const ledgerEntries: SessionEntry[] = [];
|
|
78
87
|
for (const command of commands) {
|
|
79
88
|
const result = await runShellCommand(command, cwd, runner.timeoutMs ?? timeoutMs, env);
|
|
80
89
|
stdout = appendLimited(stdout, result.stdout);
|
|
@@ -83,6 +92,7 @@ export async function runExternalCommandRunner(
|
|
|
83
92
|
usage = addTokenStreamUsage(usage, result.usage);
|
|
84
93
|
streamInvariants = addStreamInvariants(streamInvariants, result.streamInvariants);
|
|
85
94
|
fleetLoops = addFleetLoopObservations(fleetLoops, result.fleetLoops);
|
|
95
|
+
ledgerEntries.push(...result.ledgerEntries);
|
|
86
96
|
if (result.exitCode !== 0) {
|
|
87
97
|
return {
|
|
88
98
|
assignmentId: null,
|
|
@@ -99,6 +109,7 @@ export async function runExternalCommandRunner(
|
|
|
99
109
|
"verifier.exitCode": result.exitCode,
|
|
100
110
|
},
|
|
101
111
|
artifacts: {},
|
|
112
|
+
ledgerEntries,
|
|
102
113
|
};
|
|
103
114
|
}
|
|
104
115
|
}
|
|
@@ -117,6 +128,7 @@ export async function runExternalCommandRunner(
|
|
|
117
128
|
"verifier.exitCode": 0,
|
|
118
129
|
},
|
|
119
130
|
artifacts: {},
|
|
131
|
+
ledgerEntries,
|
|
120
132
|
};
|
|
121
133
|
}
|
|
122
134
|
|
|
@@ -137,6 +149,7 @@ export function runShellCommand(
|
|
|
137
149
|
const usageFold = createTokenUsageFold();
|
|
138
150
|
const streamFold = createStreamInvariantFold();
|
|
139
151
|
const fleetLoopFold = createFleetLoopFold();
|
|
152
|
+
const callLedgerFold = createEvalCallLedgerFold();
|
|
140
153
|
let timedOut = false;
|
|
141
154
|
let settled = false;
|
|
142
155
|
const child = spawn(command, {
|
|
@@ -155,6 +168,7 @@ export function runShellCommand(
|
|
|
155
168
|
usageFold.push(chunk);
|
|
156
169
|
streamFold.push(chunk);
|
|
157
170
|
fleetLoopFold.push(chunk);
|
|
171
|
+
callLedgerFold.push(chunk);
|
|
158
172
|
});
|
|
159
173
|
child.stderr.on("data", (chunk: string) => {
|
|
160
174
|
stderr = appendLimited(stderr, chunk);
|
|
@@ -176,6 +190,7 @@ export function runShellCommand(
|
|
|
176
190
|
usage: usageFold.usage(),
|
|
177
191
|
streamInvariants: streamFold.invariants(),
|
|
178
192
|
fleetLoops: fleetLoopFold.observation(),
|
|
193
|
+
ledgerEntries: callLedgerFold.entries(),
|
|
179
194
|
stderr,
|
|
180
195
|
wallTimeMs: Math.round(performance.now() - started),
|
|
181
196
|
timedOut,
|
|
@@ -284,9 +299,11 @@ function compactMetricEvent(event: Record<string, unknown>): Record<string, unkn
|
|
|
284
299
|
toolName,
|
|
285
300
|
...(toolName === "dispatch" && isRecord(event.args)
|
|
286
301
|
? { args: event.args }
|
|
287
|
-
: toolName === "
|
|
288
|
-
? { args:
|
|
289
|
-
:
|
|
302
|
+
: toolName === "read" && isRecord(event.args)
|
|
303
|
+
? { args: boundedReadArgs(event.args) }
|
|
304
|
+
: toolName === "code_nav" && isRecord(event.args)
|
|
305
|
+
? { args: { mode: event.args.mode } }
|
|
306
|
+
: {}),
|
|
290
307
|
};
|
|
291
308
|
}
|
|
292
309
|
if (type === "tool_execution_end") {
|
|
@@ -313,6 +330,14 @@ function compactMetricEvent(event: Record<string, unknown>): Record<string, unkn
|
|
|
313
330
|
};
|
|
314
331
|
}
|
|
315
332
|
|
|
333
|
+
function boundedReadArgs(args: Record<string, unknown>): Record<string, string> {
|
|
334
|
+
for (const field of ["path", "filePath", "file_path"]) {
|
|
335
|
+
const value = args[field];
|
|
336
|
+
if (typeof value === "string" && value.length > 0) return { [field]: value.slice(0, 4_096) };
|
|
337
|
+
}
|
|
338
|
+
return {};
|
|
339
|
+
}
|
|
340
|
+
|
|
316
341
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
317
342
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
318
343
|
}
|