@iowarp/clio-coder 0.3.8 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +7 -3
- package/dist/{acp-U67UHUK2.js → acp-7LOELQFP.js} +6 -6
- package/dist/{agents-YU6SGALZ.js → agents-FIBG2SHA.js} +27 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5ZPJOIVG.js → auth-OI4LIH2I.js} +11 -12
- package/dist/{builtins-C6JMZVV6.js → builtins-AD25UL3C.js} +5 -5
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-4SPRNWDE.js → chunk-3DUR4WUA.js} +15 -15
- package/dist/{chunk-VHN4MY6O.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-5DHKRSMQ.js → chunk-3UUY7R3Z.js} +11 -7
- package/dist/{chunk-IGWKHNIQ.js → chunk-3V5AYSEQ.js} +8 -8
- package/dist/{chunk-FHJEP5SW.js → chunk-465CC7FK.js} +8 -5
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-A3WNZD3P.js → chunk-4H6ULJ3H.js} +67 -21
- package/dist/{chunk-TB5666IT.js → chunk-4LJX2PUC.js} +3 -3
- package/dist/{chunk-XWSF374K.js → chunk-56KB5IJP.js} +2 -2
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-2HEJ2F35.js → chunk-5HFBWUMU.js} +20 -8
- package/dist/{chunk-WNIJTQQK.js → chunk-5PVQ4SRS.js} +78 -6
- package/dist/{chunk-DYIM5TJT.js → chunk-5QKCQQ3E.js} +262 -6
- package/dist/{chunk-TYPGUK6W.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/{chunk-IIZWH4XA.js → chunk-774ILSRL.js} +2 -2
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-TANS5ZJS.js → chunk-AD7Y7STJ.js} +3 -3
- package/dist/{chunk-RWSI4YD7.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-DGSYXYMX.js → chunk-AMKHQW3C.js} +2 -2
- package/dist/{chunk-VWZOAB7K.js → chunk-B5XRQOLB.js} +7 -7
- package/dist/{chunk-WXY7KU3G.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-PMDBGQSJ.js → chunk-CA42X6KT.js} +2 -2
- package/dist/{chunk-HLE42MG7.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-5Q2VVUKB.js → chunk-DG4M6ZUE.js} +3 -3
- package/dist/{chunk-ME6CCNFO.js → chunk-EBOC7MT3.js} +6 -6
- package/dist/{chunk-MXKJU4JB.js → chunk-ECUO3KDP.js} +48 -7
- package/dist/{chunk-26LEYJZH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-7RGZWPB6.js → chunk-GAYUJ7LE.js} +67 -13
- package/dist/{chunk-VCBR6CU7.js → chunk-HAY4ZE2P.js} +2 -2
- package/dist/{chunk-FBVTI2TJ.js → chunk-HCBCAYZU.js} +11 -130
- package/dist/{chunk-WSB3FPX7.js → chunk-HJB5IUKP.js} +32 -136
- package/dist/{chunk-J3YUBZWY.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-E77JEWSD.js → chunk-HPCTNZM2.js} +6 -36
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/{chunk-NMPKI6XL.js → chunk-JEQQR47K.js} +37 -18
- package/dist/{chunk-3BINW3FP.js → chunk-KV2AOLDF.js} +24 -4
- package/dist/{chunk-YS5VLNH5.js → chunk-LXPJXFM5.js} +7 -7
- package/dist/{chunk-7RFXX52T.js → chunk-MIX5N5AC.js} +271 -41
- package/dist/{chunk-K4XHGFR5.js → chunk-MLOK6ZOS.js} +1297 -218
- package/dist/{chunk-ZNLWCMVZ.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-5H3GB5BO.js → chunk-N3PBVRTZ.js} +4 -382
- package/dist/{chunk-2HFZQUHL.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-GU2UIAFZ.js → chunk-NQ6UCCOD.js} +3 -3
- package/dist/{chunk-N22QMJKY.js → chunk-NZU6YDNV.js} +4 -4
- package/dist/{chunk-ZVJ5BLO2.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-VAWNZU7Z.js → chunk-P3JGPQFL.js} +2 -2
- package/dist/{chunk-IJ7RPIYJ.js → chunk-PNY46YEY.js} +20 -3
- package/dist/{chunk-U6MBIEMB.js → chunk-PZ4I4JE2.js} +56 -35
- package/dist/{chunk-GPIEI3LY.js → chunk-QQ7EKM72.js} +2 -2
- package/dist/{chunk-WLFILSD5.js → chunk-R7LNVMCS.js} +66 -28
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-TT36MB5S.js → chunk-RKRLDWD3.js} +3 -1
- package/dist/{chunk-TTHACPOM.js → chunk-S4COXYBG.js} +456 -18
- package/dist/{chunk-WWCZ5F23.js → chunk-T3Z6VAAF.js} +69 -10
- package/dist/{chunk-GN57SG4G.js → chunk-TD7UE2L5.js} +9 -7
- package/dist/{chunk-TLQJPP24.js → chunk-TEO2TLVN.js} +523 -322
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-KTYTFRMB.js → chunk-VKBMFOYV.js} +17 -15
- package/dist/{chunk-PT7HYKEM.js → chunk-VO2LKSTM.js} +2 -2
- package/dist/{chunk-P43ETTHK.js → chunk-VPTUJU4P.js} +2 -2
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-EMYUUSFG.js → chunk-WXCJ7VME.js} +5 -5
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-JOZYP4GM.js → chunk-YKOFT37S.js} +5 -5
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +27 -27
- package/dist/{clio-QVTYJ57A.js → clio-LT5V7SSZ.js} +6 -6
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +4 -4
- package/dist/{config-LW5IJFQN.js → config-RXS5T3JT.js} +71 -43
- package/dist/{configure-7XIZCOU4.js → configure-2WYWSCSD.js} +14 -15
- package/dist/{context-Y6Y7QPR6.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-L3WL3X7K.js → context-MVOORGMF.js} +34 -33
- package/dist/{context-N52ZA626.js → context-PALKKQYL.js} +20 -20
- package/dist/{context-clear-MBQRLSDQ.js → context-clear-N2WOYZ2K.js} +34 -33
- package/dist/{context-index-HVMFQHK3.js → context-index-HNG3MOME.js} +2 -2
- package/dist/{context-working-set-GS6DSO7F.js → context-working-set-MIEVECVZ.js} +10 -11
- package/dist/{dispatch-runner-22ZCNOM3.js → dispatch-runner-VVA4SRRH.js} +34 -33
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/{eval-BEC2WHDA.js → eval-IJ5VEZDJ.js} +2016 -142
- package/dist/{evidence-REJUMSKM.js → evidence-L5APPXNV.js} +29 -28
- package/dist/{evolve-PY5ZBA5K.js → evolve-RGNKFJ52.js} +29 -28
- package/dist/{extensions-HVKU65YU.js → extensions-7WYWUX5A.js} +9 -3
- package/dist/{fleet-7WZEWRFA.js → fleet-6CNVBZZP.js} +87 -55
- package/dist/{fleet-commands-UVHWM76J.js → fleet-commands-L2SXSYEI.js} +6 -6
- package/dist/{fleet-graph-6ULH7PES.js → fleet-graph-2J3OOIPO.js} +14 -12
- package/dist/{fleet-preflight-J53T6CCE.js → fleet-preflight-CZRJ4JP5.js} +3 -4
- package/dist/{fleet-validate-72PC4SLA.js → fleet-validate-C5RI6DP7.js} +16 -15
- package/dist/{init-OG3TPGQG.js → init-VBN2ACVA.js} +50 -48
- package/dist/{library-CNTMPLRF.js → library-JHGUMLY2.js} +13 -11
- package/dist/{memory-6IS7F275.js → memory-K4OQIYWG.js} +31 -30
- package/dist/{models-ENRJDA5W.js → models-2NCZUWDD.js} +23 -22
- package/dist/{monitor-XLDVO7TN.js → monitor-MMVTJABD.js} +35 -34
- package/dist/{orchestrator-6KSPYRHA.js → orchestrator-ZKBPCHW6.js} +1627 -294
- package/dist/{reset-RZ4ER727.js → reset-DD5JGOY3.js} +3 -3
- package/dist/{run-Y2CNK5RU.js → run-QEGNX7FL.js} +56 -55
- package/dist/{share-A55GYP6Z.js → share-JKD3BQMW.js} +13 -11
- package/dist/{skills-ALC5J6AT.js → skills-LMQIKDOZ.js} +14 -12
- package/dist/{skills-eval-JPBEBYQU.js → skills-eval-I7X2774U.js} +34 -32
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/{support-MIETYA5E.js → support-I7LOJLIF.js} +4 -4
- package/dist/{targets-VGNXIR3S.js → targets-RUSR6B5Z.js} +58 -32
- package/dist/{terminal-lease-WOBR64YA.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{upgrade-FUSUAGHR.js → upgrade-XANW3FXB.js} +17 -16
- package/dist/{usage-N4MKVHKD.js → usage-4H7ZRXQT.js} +88 -44
- package/dist/{verifiers-YAWOJ3H2.js → verifiers-UZXNBZEB.js} +6 -6
- package/dist/{verify-LTDHYBGY.js → verify-BVKWTNDL.js} +5 -5
- package/dist/{wiki-generate-6M7GHTBJ.js → wiki-generate-MY7WV2QI.js} +49 -47
- package/dist/worker/entry.js +29 -33
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +6 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +1 -1
- package/docs/configuration-and-targets.md +30 -3
- package/docs/context-engine.md +63 -4
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +2 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +11 -10
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +4 -4
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +53 -2
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +19 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +19 -3
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +87 -0
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +2 -1
- package/src/cli/agents.ts +1 -1
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +3 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet.ts +32 -3
- package/src/cli/targets.ts +44 -13
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/core/bus-events.ts +29 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/config.ts +18 -0
- package/src/core/defaults.ts +36 -6
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +11 -0
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/extension.ts +166 -42
- package/src/domains/dispatch/fleet-run.ts +23 -3
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +3 -0
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +20 -3
- package/src/domains/dispatch/write-boundary.ts +62 -1
- package/src/domains/eval/artifacts/store.ts +62 -0
- package/src/domains/eval/compare/behavioral.ts +224 -0
- package/src/domains/eval/compare/compare.ts +355 -2
- package/src/domains/eval/compare/envelope.ts +128 -0
- package/src/domains/eval/compare/gates.ts +24 -6
- package/src/domains/eval/compare/thresholds.ts +30 -3
- package/src/domains/eval/execution-provenance.ts +240 -0
- package/src/domains/eval/metrics/aggregate.ts +136 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
- package/src/domains/eval/metrics/tracked.ts +413 -0
- package/src/domains/eval/provenance.ts +117 -0
- package/src/domains/eval/reports/comparison.ts +128 -0
- package/src/domains/eval/reports/junit.ts +17 -3
- package/src/domains/eval/reports/markdown.ts +3 -3
- package/src/domains/eval/reports/text.ts +14 -0
- package/src/domains/eval/run-compare.ts +20 -0
- package/src/domains/eval/runners/clio-run.ts +127 -0
- package/src/domains/eval/runners/external-command.ts +28 -3
- package/src/domains/eval/schema/adapter.ts +111 -0
- package/src/domains/eval/schema/artifact.ts +20 -0
- package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
- package/src/domains/eval/schema/behavioral.ts +520 -0
- package/src/domains/eval/schema/execution-envelope.ts +194 -0
- package/src/domains/eval/schema/serving.ts +74 -0
- package/src/domains/eval/schema/suite.ts +38 -8
- package/src/domains/eval/schema/validate.ts +58 -3
- package/src/domains/eval/schema/verdict.ts +237 -0
- package/src/domains/eval/suites/resolve.ts +2 -0
- package/src/domains/eval/suites/run.ts +264 -33
- package/src/domains/eval/verifiers/command.ts +2 -1
- package/src/domains/eval/workspaces/temp-copy.ts +145 -13
- package/src/domains/evidence/build.ts +2 -13
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +2 -2
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +38 -3
- package/src/domains/extensions/resources.ts +1 -1
- package/src/domains/extensions/state.ts +12 -3
- package/src/domains/extensions/types.ts +2 -0
- package/src/domains/lifecycle/doctor.ts +69 -1
- package/src/domains/memory/index.ts +14 -0
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +77 -8
- package/src/domains/memory/task-memory-spend.ts +131 -0
- package/src/domains/memory/task-memory-status.ts +7 -0
- package/src/domains/memory/task-memory-telemetry.ts +2 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +69 -5
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/index.ts +7 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +192 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/providers/endpoint-capacity.ts +96 -0
- package/src/domains/providers/index.ts +10 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
- package/src/domains/providers/runtime-resolution.ts +8 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/prompts/loader.ts +95 -33
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/run-effects.ts +35 -4
- package/src/domains/session/context-accounting.ts +52 -1
- package/src/domains/session/context-ledger.ts +37 -13
- package/src/domains/session/index.ts +6 -0
- package/src/domains/session/prompt-cache.ts +140 -0
- package/src/domains/session/prompt-manifest.ts +42 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/provider-payload.ts +29 -1
- package/src/entry/orchestrator.ts +176 -30
- package/src/interactive/chat-loop-messages.ts +26 -7
- package/src/interactive/chat-loop.ts +318 -41
- package/src/interactive/chat-panel.ts +62 -8
- package/src/interactive/clio-editor.ts +45 -8
- package/src/interactive/context-activity.ts +5 -1
- package/src/interactive/context-meter.ts +1 -1
- package/src/interactive/context-overlay.ts +40 -10
- package/src/interactive/cost-overlay.ts +64 -6
- package/src/interactive/dispatch-board.ts +84 -12
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +24 -1
- package/src/interactive/interactive-input-runtime.ts +8 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +27 -4
- package/src/interactive/memory-overlay.ts +8 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/overlay-general-openers.ts +16 -0
- package/src/interactive/overlay-key-routing.ts +38 -0
- package/src/interactive/overlay-lifecycle.ts +38 -5
- package/src/interactive/overlay-permission-lifecycle.ts +22 -2
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlays/ask-user.ts +91 -19
- package/src/interactive/overlays/help-reference.ts +4 -0
- package/src/interactive/overlays/prompts.ts +11 -1
- package/src/interactive/overlays/settings.ts +35 -1
- package/src/interactive/permission-hint.ts +34 -2
- package/src/interactive/permission-overlay.ts +159 -9
- package/src/interactive/prewarm.ts +197 -0
- package/src/interactive/render-trace.ts +162 -15
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/status/controller.ts +11 -0
- package/src/interactive/status/state-machine.ts +54 -2
- package/src/interactive/status/types.ts +7 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/turn-context.ts +299 -31
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/view-overlay.ts +28 -3
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-plan.ts +17 -9
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/registry.ts +16 -0
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/doctor-M7YEDGAE.js +0 -91
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
type SessionEntry,
|
|
25
25
|
} from "../session/index.js";
|
|
26
26
|
import { attributeEvidenceFailure } from "./failure-attribution.js";
|
|
27
|
+
import { renderEvidenceFindingsMarkdown } from "./findings-markdown.js";
|
|
27
28
|
import { compareCodepoints as compareStrings } from "./ordering.js";
|
|
28
29
|
import { extractRunProvenance, provenanceTranscriptLines } from "./provenance.js";
|
|
29
30
|
import { createRedactionTally, redactSecretsDeep, redactSecretsText } from "./redact.js";
|
|
@@ -159,7 +160,6 @@ export async function buildEvidence(options: BuildEvidenceOptions): Promise<Evid
|
|
|
159
160
|
evidenceId,
|
|
160
161
|
runSources,
|
|
161
162
|
gateDecisions: gateDecisions.decisions,
|
|
162
|
-
auditRows: auditLinks.rows,
|
|
163
163
|
validationEvidence,
|
|
164
164
|
});
|
|
165
165
|
const findings = buildFindings(runSources, trustStatusRaw, sessionLinks, auditLinks, protectedArtifactsRaw);
|
|
@@ -630,7 +630,7 @@ async function writeEvidenceFiles(
|
|
|
630
630
|
await writeJson(join(directory, "trust-status.json"), trustStatus);
|
|
631
631
|
await writeJson(join(directory, "protected-artifacts.json"), protectedArtifacts);
|
|
632
632
|
await writeJson(join(directory, "findings.json"), findingsFile(overview.evidenceId, [...findings]));
|
|
633
|
-
await writeFile(join(directory, "findings.md"),
|
|
633
|
+
await writeFile(join(directory, "findings.md"), renderEvidenceFindingsMarkdown(findings, trustStatus), "utf8");
|
|
634
634
|
}
|
|
635
635
|
|
|
636
636
|
function rawTraceRows(runSources: ReadonlyArray<EvidenceRunSource>): EvidenceRawTraceRow[] {
|
|
@@ -1493,17 +1493,6 @@ function axisFindings(offset: number, source: EvidenceRunSource, status: Canonic
|
|
|
1493
1493
|
return out;
|
|
1494
1494
|
}
|
|
1495
1495
|
|
|
1496
|
-
function renderFindings(findings: ReadonlyArray<EvidenceFinding>): string {
|
|
1497
|
-
if (findings.length === 0) return "# Findings\n\nNo findings.\n";
|
|
1498
|
-
const lines = ["# Findings", ""];
|
|
1499
|
-
for (const item of findings) {
|
|
1500
|
-
const run = item.runId === null ? "" : ` run=${item.runId}`;
|
|
1501
|
-
lines.push(`- ${item.id} [${item.severity}] ${item.tag}${run}: ${item.message}`);
|
|
1502
|
-
}
|
|
1503
|
-
lines.push("");
|
|
1504
|
-
return `${lines.join("\n")}\n`;
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
1496
|
async function readRunLedger(stateDir: string): Promise<RunLedgerRows> {
|
|
1508
1497
|
const target = join(stateDir, "runs.json");
|
|
1509
1498
|
let raw: string;
|
|
@@ -5,6 +5,7 @@ import { readGateDecisionArtifactsForRunIds } from "../dispatch/index.js";
|
|
|
5
5
|
import { redactArtifactForStorage } from "../eval/artifacts/redact.js";
|
|
6
6
|
import type { EvalCommandResult, EvalRunArtifact, EvalRunRecord } from "../eval/index.js";
|
|
7
7
|
import { loadEvalArtifact } from "../eval/index.js";
|
|
8
|
+
import { renderEvidenceFindingsMarkdown } from "./findings-markdown.js";
|
|
8
9
|
import { compareCodepoints as compareStrings } from "./ordering.js";
|
|
9
10
|
import { buildEvidenceTrustStatusFile } from "./run-trust.js";
|
|
10
11
|
import { evidenceDirectory, findingsFile } from "./store.js";
|
|
@@ -95,7 +96,6 @@ export async function buildEvalEvidence(options: BuildEvalEvidenceOptions): Prom
|
|
|
95
96
|
evidenceId,
|
|
96
97
|
runSources: linkedRuns.runSources,
|
|
97
98
|
gateDecisions: gateDecisions.decisions,
|
|
98
|
-
auditRows: linkedRuns.auditRows,
|
|
99
99
|
});
|
|
100
100
|
await writeEvalEvidenceFiles(
|
|
101
101
|
directory,
|
|
@@ -258,7 +258,7 @@ async function writeEvalEvidenceFiles(
|
|
|
258
258
|
await writeJson(join(directory, "protected-artifacts.json"), emptyProtected);
|
|
259
259
|
await writeJson(join(directory, "eval-result.json"), redactedArtifact);
|
|
260
260
|
await writeJson(join(directory, "findings.json"), findingsFile(overview.evidenceId, [...findings]));
|
|
261
|
-
await writeFile(join(directory, "findings.md"),
|
|
261
|
+
await writeFile(join(directory, "findings.md"), renderEvidenceFindingsMarkdown(findings, trustStatus), "utf8");
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
function rawEvalTraceRows(artifact: EvalRunArtifact): EvidenceEvalRawTraceRow[] {
|
|
@@ -360,16 +360,6 @@ function renderEvalTranscript(artifact: EvalRunArtifact, overview: EvidenceOverv
|
|
|
360
360
|
return `${lines.join("\n")}\n`;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
function renderFindings(findings: ReadonlyArray<EvidenceFinding>): string {
|
|
364
|
-
if (findings.length === 0) return "No findings.\n";
|
|
365
|
-
const lines = ["# Findings", ""];
|
|
366
|
-
for (const finding of findings) {
|
|
367
|
-
lines.push(`- ${finding.id} [${finding.severity}] ${finding.tag}: ${finding.message}`);
|
|
368
|
-
}
|
|
369
|
-
lines.push("");
|
|
370
|
-
return `${lines.join("\n")}\n`;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
363
|
async function writeJson(path: string, value: unknown): Promise<void> {
|
|
374
364
|
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
375
365
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { formatTrustAxes, formatTrustSummary, trustVerdict } from "./trust-projection.js";
|
|
2
|
+
import type { EvidenceFinding, EvidenceTrustStatusFile } from "./types.js";
|
|
3
|
+
|
|
4
|
+
/** Render the canonical verdict before the diagnostic records in every evidence bundle. */
|
|
5
|
+
export function renderEvidenceFindingsMarkdown(
|
|
6
|
+
findings: ReadonlyArray<EvidenceFinding>,
|
|
7
|
+
trustStatus: EvidenceTrustStatusFile,
|
|
8
|
+
): string {
|
|
9
|
+
const lines = ["# Findings", "", "## Canonical trust", ""];
|
|
10
|
+
if (trustStatus.runs.length === 0) {
|
|
11
|
+
lines.push("No linked run trust status.");
|
|
12
|
+
} else {
|
|
13
|
+
for (const run of trustStatus.runs) {
|
|
14
|
+
lines.push(
|
|
15
|
+
`- run=${run.runId}`,
|
|
16
|
+
` tier: ${trustVerdict(run.status)}`,
|
|
17
|
+
` summary: ${formatTrustSummary(run.status)}`,
|
|
18
|
+
` axes: ${formatTrustAxes(run.status)}`,
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
lines.push("", "## Finding records", "");
|
|
23
|
+
if (findings.length === 0) {
|
|
24
|
+
lines.push("No findings.");
|
|
25
|
+
} else {
|
|
26
|
+
for (const item of findings) {
|
|
27
|
+
const run = item.runId === null ? "" : ` run=${item.runId}`;
|
|
28
|
+
lines.push(`- ${item.id} [${item.severity}] ${item.tag}${run}: ${item.message}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
lines.push("");
|
|
32
|
+
return `${lines.join("\n")}\n`;
|
|
33
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { type GateDecisionArtifact, verifyGateDecisionArtifact } from "../dispatch/gate-decisions.js";
|
|
2
2
|
import type { RunEnvelope, RunReceipt } from "../dispatch/types.js";
|
|
3
|
-
import type { FinishContractAssessment, FinishContractEvidenceKind } from "../safety/finish-contract.js";
|
|
4
3
|
import { compareCodepoints as compareStrings } from "./ordering.js";
|
|
5
4
|
import {
|
|
6
|
-
adaptFinishContractCompletionStatus,
|
|
7
5
|
adaptGateDecisionReviewStatus,
|
|
8
6
|
adaptGroundedEvidenceValidationStatus,
|
|
9
7
|
type CanonicalTrustStatus,
|
|
@@ -12,7 +10,7 @@ import {
|
|
|
12
10
|
isTrustStatusIdentifier,
|
|
13
11
|
type TrustArtifactReference,
|
|
14
12
|
} from "./trust-status.js";
|
|
15
|
-
import { EVIDENCE_VERSION, type
|
|
13
|
+
import { EVIDENCE_VERSION, type EvidenceTrustStatusFile } from "./types.js";
|
|
16
14
|
|
|
17
15
|
export interface EvidenceRunTrustSource {
|
|
18
16
|
envelope: RunEnvelope;
|
|
@@ -23,15 +21,15 @@ export interface BuildEvidenceTrustStatusInput {
|
|
|
23
21
|
evidenceId: string;
|
|
24
22
|
runSources: ReadonlyArray<EvidenceRunTrustSource>;
|
|
25
23
|
gateDecisions: ReadonlyArray<GateDecisionArtifact>;
|
|
26
|
-
auditRows: ReadonlyArray<EvidenceAuditLinkedRow>;
|
|
27
24
|
validationEvidence?: ReadonlyMap<string, ReadonlyArray<TrustArtifactReference>>;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
/**
|
|
31
28
|
* Compose every evidence-linked trust axis at one pure boundary. Both the
|
|
32
29
|
* run/session and eval builders call this function, so the same authenticated
|
|
33
|
-
* receipt
|
|
34
|
-
*
|
|
30
|
+
* receipt and gate decision cannot project differently merely because the
|
|
31
|
+
* receipt was bundled through a different entry point. Completion evidence
|
|
32
|
+
* stays receipt-derived so an audit row cannot change one surface alone.
|
|
35
33
|
*/
|
|
36
34
|
export function buildEvidenceTrustStatusFile(input: BuildEvidenceTrustStatusInput): EvidenceTrustStatusFile {
|
|
37
35
|
return {
|
|
@@ -44,7 +42,6 @@ export function buildEvidenceTrustStatusFile(input: BuildEvidenceTrustStatusInpu
|
|
|
44
42
|
evidenceId: input.evidenceId,
|
|
45
43
|
source,
|
|
46
44
|
gateDecisions: input.gateDecisions,
|
|
47
|
-
auditRows: input.auditRows,
|
|
48
45
|
validationEvidence: input.validationEvidence?.get(source.envelope.id) ?? [],
|
|
49
46
|
}),
|
|
50
47
|
})),
|
|
@@ -55,22 +52,15 @@ interface ComposeEvidenceRunTrustInput {
|
|
|
55
52
|
evidenceId: string;
|
|
56
53
|
source: EvidenceRunTrustSource;
|
|
57
54
|
gateDecisions: ReadonlyArray<GateDecisionArtifact>;
|
|
58
|
-
auditRows: ReadonlyArray<EvidenceAuditLinkedRow>;
|
|
59
55
|
validationEvidence: ReadonlyArray<TrustArtifactReference>;
|
|
60
56
|
}
|
|
61
57
|
|
|
62
58
|
function composeEvidenceRunTrustStatus(input: ComposeEvidenceRunTrustInput): CanonicalTrustStatus {
|
|
63
59
|
const inspection = inspectRunReceiptTrustStatus(input.source.receipt, input.source.envelope);
|
|
64
|
-
// A receipt that was presented and rejected authenticates nothing about the
|
|
65
|
-
// run it names. Its integrity failure stays on `artifactIntegrity`; no other
|
|
66
|
-
// axis may reach a trust-granting state on the strength of that run's own
|
|
67
|
-
// self-reports. A missing or unchecked receipt is not a rejection.
|
|
68
|
-
const receiptRejected = inspection.status.artifactIntegrity.state === "failed";
|
|
69
60
|
let status = inspection.status;
|
|
70
|
-
// Only independently observed executions ground validation.
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
// enforces are listed under "composition rules" in docs/evidence-and-memory.md.
|
|
61
|
+
// Only independently observed executions ground validation. A completion
|
|
62
|
+
// contract audit row is the run's own self-report and cannot override the
|
|
63
|
+
// receipt-derived completion axis on this surface alone.
|
|
74
64
|
const grounded = input.validationEvidence.filter((artifact) => isTrustStatusIdentifier(artifact.id));
|
|
75
65
|
if (
|
|
76
66
|
grounded.length > 0 &&
|
|
@@ -106,25 +96,6 @@ function composeEvidenceRunTrustStatus(input: ComposeEvidenceRunTrustInput): Can
|
|
|
106
96
|
independentReview: adaptGateDecisionReviewStatus(gate, input.source.envelope.id, verification),
|
|
107
97
|
});
|
|
108
98
|
}
|
|
109
|
-
|
|
110
|
-
const finish = latestFinishContractForRun(input.auditRows, input.source.envelope.id);
|
|
111
|
-
if (finish !== null) {
|
|
112
|
-
const completion = adaptFinishContractCompletionStatus(finish.assessment, {
|
|
113
|
-
sourceId: finish.sourceId,
|
|
114
|
-
artifacts: finish.artifacts,
|
|
115
|
-
});
|
|
116
|
-
status = composeTrustStatus(status, {
|
|
117
|
-
completionEvidence:
|
|
118
|
-
receiptRejected && completion.state === "evidenced"
|
|
119
|
-
? {
|
|
120
|
-
state: "unknown",
|
|
121
|
-
source: { kind: "finish_contract", id: finish.sourceId },
|
|
122
|
-
authority: { kind: "clio", id: "finish-contract" },
|
|
123
|
-
artifacts: finish.artifacts,
|
|
124
|
-
}
|
|
125
|
-
: completion,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
99
|
return status;
|
|
129
100
|
}
|
|
130
101
|
|
|
@@ -142,80 +113,3 @@ function latestGateDecisionForRun(
|
|
|
142
113
|
.at(-1) ?? null
|
|
143
114
|
);
|
|
144
115
|
}
|
|
145
|
-
|
|
146
|
-
interface EvidenceFinishContractProjection {
|
|
147
|
-
sourceId: string;
|
|
148
|
-
assessment: FinishContractAssessment;
|
|
149
|
-
artifacts: TrustArtifactReference[];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function latestFinishContractForRun(
|
|
153
|
-
rows: ReadonlyArray<EvidenceAuditLinkedRow>,
|
|
154
|
-
runId: string,
|
|
155
|
-
): EvidenceFinishContractProjection | null {
|
|
156
|
-
const candidates = rows
|
|
157
|
-
.filter((row) => row.auditKind === "completion_contract" && row.runId === runId && row.confidence === "exact")
|
|
158
|
-
.flatMap((row) => {
|
|
159
|
-
const assessment = finishContractAssessmentFromAudit(row.row);
|
|
160
|
-
if (assessment === null) return [];
|
|
161
|
-
const correlationId = readOptionalString(row.row.correlationId) ?? `${runId}:completion-contract`;
|
|
162
|
-
// A malformed row is dropped from the trust projection rather than
|
|
163
|
-
// thrown out of the whole forensic build. `readAuditRows` already
|
|
164
|
-
// reports the malformed line, so the bundle stays honest about it.
|
|
165
|
-
if (!isTrustStatusIdentifier(correlationId)) return [];
|
|
166
|
-
const turnId = readOptionalString(row.row.turnId);
|
|
167
|
-
const artifacts: TrustArtifactReference[] = [
|
|
168
|
-
{ kind: "finish_contract_evidence", id: correlationId },
|
|
169
|
-
...(turnId === null || !isTrustStatusIdentifier(turnId) ? [] : [{ kind: "session_entry" as const, id: turnId }]),
|
|
170
|
-
];
|
|
171
|
-
return [{ sourceId: correlationId, assessment, artifacts, timestamp: row.ts ?? "" }];
|
|
172
|
-
})
|
|
173
|
-
.sort(
|
|
174
|
-
(left, right) => compareStrings(left.timestamp, right.timestamp) || compareStrings(left.sourceId, right.sourceId),
|
|
175
|
-
);
|
|
176
|
-
const latest = candidates.at(-1);
|
|
177
|
-
return latest === undefined
|
|
178
|
-
? null
|
|
179
|
-
: { sourceId: latest.sourceId, assessment: latest.assessment, artifacts: latest.artifacts };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function finishContractAssessmentFromAudit(row: Record<string, unknown>): FinishContractAssessment | null {
|
|
183
|
-
const reason = readOptionalString(row.reason);
|
|
184
|
-
const decision = readOptionalString(row.decision);
|
|
185
|
-
const mutatedPaths = Array.isArray(row.mutatedPaths)
|
|
186
|
-
? row.mutatedPaths.filter((entry): entry is string => typeof entry === "string")
|
|
187
|
-
: [];
|
|
188
|
-
const turnId = readOptionalString(row.turnId);
|
|
189
|
-
const evidenceKinds = Array.isArray(row.evidenceKinds) ? row.evidenceKinds.filter(isFinishContractEvidenceKind) : [];
|
|
190
|
-
const evidence = evidenceKinds.map((kind) => ({
|
|
191
|
-
kind,
|
|
192
|
-
summary: "recorded by the finish contract",
|
|
193
|
-
...(turnId === null ? {} : { turnId }),
|
|
194
|
-
}));
|
|
195
|
-
if (decision === "ok" && reason === "no_mutation") {
|
|
196
|
-
return { kind: "ok", reason, evidence: [], mutatedPaths };
|
|
197
|
-
}
|
|
198
|
-
if (decision === "ok" && reason === "validation_evidence") {
|
|
199
|
-
return { kind: "ok", reason, evidence, mutatedPaths };
|
|
200
|
-
}
|
|
201
|
-
if (decision === "ok" && reason === "explicit_limitation") {
|
|
202
|
-
return { kind: "ok", reason, evidence: [], mutatedPaths };
|
|
203
|
-
}
|
|
204
|
-
if (decision === "engage" && reason === "unvalidated_mutation") {
|
|
205
|
-
return { kind: "engage", reason, message: "finish contract engaged", evidence: [], mutatedPaths };
|
|
206
|
-
}
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function isFinishContractEvidenceKind(value: unknown): value is FinishContractEvidenceKind {
|
|
211
|
-
return value === "validation_command" || value === "protected_artifact" || value === "dispatch_receipt";
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* A blank or whitespace-only optional field is absent, not an identifier. The
|
|
216
|
-
* `??` fallbacks below depend on it, and an empty string would otherwise reach
|
|
217
|
-
* `normalizeArtifactReference` and abort the entire bundle.
|
|
218
|
-
*/
|
|
219
|
-
function readOptionalString(value: unknown): string | null {
|
|
220
|
-
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
221
|
-
}
|
|
@@ -184,9 +184,9 @@ export function formatTrustSummary(status: CanonicalTrustStatus): string {
|
|
|
184
184
|
].join("; ");
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
/** The body under
|
|
187
|
+
/** The tier and body under one versioned label for receipt-facing surfaces. */
|
|
188
188
|
export function formatTrustSummaryLine(status: CanonicalTrustStatus): string {
|
|
189
|
-
return `trust v${TRUST_SUMMARY_VERSION}: ${formatTrustSummary(status)}`;
|
|
189
|
+
return `trust v${TRUST_SUMMARY_VERSION}: ${trustVerdict(status)}; ${formatTrustSummary(status)}`;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/**
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SemVer range evaluation for extension compatibility.
|
|
3
|
+
*
|
|
4
|
+
* Extension manifests are untrusted package input, so this parser is bounded
|
|
5
|
+
* and fail-closed. It accepts the ordinary npm range vocabulary authors use in
|
|
6
|
+
* manifests: comparator sets, `||`, exact and partial versions, wildcards,
|
|
7
|
+
* hyphen ranges, caret ranges, and tilde ranges. Tags and malformed or
|
|
8
|
+
* overlong expressions are rejected instead of being treated as compatible.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { getVersionInfo } from "../lifecycle/version.js";
|
|
12
|
+
|
|
13
|
+
const MAX_RANGE_CHARS = 256;
|
|
14
|
+
|
|
15
|
+
interface SemVer {
|
|
16
|
+
major: number;
|
|
17
|
+
minor: number;
|
|
18
|
+
patch: number;
|
|
19
|
+
prerelease: ReadonlyArray<string | number>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface PartialVersion {
|
|
23
|
+
major: number | null;
|
|
24
|
+
minor: number | null;
|
|
25
|
+
patch: number | null;
|
|
26
|
+
prerelease: ReadonlyArray<string | number>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type Predicate = (version: SemVer) => boolean;
|
|
30
|
+
|
|
31
|
+
const NUMERIC = "(?:0|[1-9]\\d*)";
|
|
32
|
+
const IDENTIFIER = "[0-9A-Za-z-]+";
|
|
33
|
+
const VERSION_RE = new RegExp(
|
|
34
|
+
`^v?(${NUMERIC})\\.(${NUMERIC})\\.(${NUMERIC})(?:-(${IDENTIFIER}(?:\\.${IDENTIFIER})*))?(?:\\+${IDENTIFIER}(?:\\.${IDENTIFIER})*)?$`,
|
|
35
|
+
);
|
|
36
|
+
const PARTIAL_RE = new RegExp(
|
|
37
|
+
`^v?(${NUMERIC}|[xX*])(?:\\.(${NUMERIC}|[xX*]))?(?:\\.(${NUMERIC}|[xX*]))?(?:-(${IDENTIFIER}(?:\\.${IDENTIFIER})*))?(?:\\+${IDENTIFIER}(?:\\.${IDENTIFIER})*)?$`,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
function prerelease(value: string | undefined): ReadonlyArray<string | number> | null {
|
|
41
|
+
if (value === undefined) return [];
|
|
42
|
+
const out: Array<string | number> = [];
|
|
43
|
+
for (const identifier of value.split(".")) {
|
|
44
|
+
if (identifier.length === 0) return null;
|
|
45
|
+
if (/^\d+$/u.test(identifier)) {
|
|
46
|
+
if (identifier.length > 1 && identifier.startsWith("0")) return null;
|
|
47
|
+
const parsed = Number(identifier);
|
|
48
|
+
if (!Number.isSafeInteger(parsed)) return null;
|
|
49
|
+
out.push(parsed);
|
|
50
|
+
} else {
|
|
51
|
+
out.push(identifier);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseSemVer(value: string): SemVer | null {
|
|
58
|
+
const match = VERSION_RE.exec(value);
|
|
59
|
+
if (match === null) return null;
|
|
60
|
+
const parsedPrerelease = prerelease(match[4]);
|
|
61
|
+
if (parsedPrerelease === null) return null;
|
|
62
|
+
const major = Number(match[1]);
|
|
63
|
+
const minor = Number(match[2]);
|
|
64
|
+
const patch = Number(match[3]);
|
|
65
|
+
if (![major, minor, patch].every(Number.isSafeInteger)) return null;
|
|
66
|
+
return { major, minor, patch, prerelease: parsedPrerelease };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parsePart(value: string | undefined): number | null {
|
|
70
|
+
if (value === undefined || value === "x" || value === "X" || value === "*") return null;
|
|
71
|
+
const parsed = Number(value);
|
|
72
|
+
return Number.isSafeInteger(parsed) ? parsed : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function parsePartial(value: string): PartialVersion | null {
|
|
76
|
+
const match = PARTIAL_RE.exec(value);
|
|
77
|
+
if (match === null) return null;
|
|
78
|
+
for (const part of match.slice(1, 4)) {
|
|
79
|
+
if (part === undefined || part === "x" || part === "X" || part === "*") continue;
|
|
80
|
+
if (!Number.isSafeInteger(Number(part))) return null;
|
|
81
|
+
}
|
|
82
|
+
const major = parsePart(match[1]);
|
|
83
|
+
const minor = parsePart(match[2]);
|
|
84
|
+
const patch = parsePart(match[3]);
|
|
85
|
+
// A wildcard closes the core. `1.x.3` is not a range with a coherent
|
|
86
|
+
// meaning, and accepting it differently from npm would be worse than a
|
|
87
|
+
// parse-time refusal.
|
|
88
|
+
if (
|
|
89
|
+
(major === null && (match[2] !== undefined || match[3] !== undefined)) ||
|
|
90
|
+
(minor === null && match[3] !== undefined)
|
|
91
|
+
) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const parsedPrerelease = prerelease(match[4]);
|
|
95
|
+
if (parsedPrerelease === null) return null;
|
|
96
|
+
if (parsedPrerelease.length > 0 && (major === null || minor === null || patch === null)) return null;
|
|
97
|
+
return { major, minor, patch, prerelease: parsedPrerelease };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function compareIdentifiers(left: string | number, right: string | number): number {
|
|
101
|
+
if (left === right) return 0;
|
|
102
|
+
if (typeof left === "number" && typeof right === "number") return left < right ? -1 : 1;
|
|
103
|
+
if (typeof left === "number") return -1;
|
|
104
|
+
if (typeof right === "number") return 1;
|
|
105
|
+
return left < right ? -1 : 1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function compare(left: SemVer, right: SemVer): number {
|
|
109
|
+
for (const key of ["major", "minor", "patch"] as const) {
|
|
110
|
+
if (left[key] !== right[key]) return left[key] < right[key] ? -1 : 1;
|
|
111
|
+
}
|
|
112
|
+
if (left.prerelease.length === 0 || right.prerelease.length === 0) {
|
|
113
|
+
if (left.prerelease.length === right.prerelease.length) return 0;
|
|
114
|
+
return left.prerelease.length === 0 ? 1 : -1;
|
|
115
|
+
}
|
|
116
|
+
const length = Math.max(left.prerelease.length, right.prerelease.length);
|
|
117
|
+
for (let index = 0; index < length; index += 1) {
|
|
118
|
+
const leftPart = left.prerelease[index];
|
|
119
|
+
const rightPart = right.prerelease[index];
|
|
120
|
+
if (leftPart === undefined || rightPart === undefined) {
|
|
121
|
+
if (leftPart === rightPart) return 0;
|
|
122
|
+
return leftPart === undefined ? -1 : 1;
|
|
123
|
+
}
|
|
124
|
+
const order = compareIdentifiers(leftPart, rightPart);
|
|
125
|
+
if (order !== 0) return order;
|
|
126
|
+
}
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function floor(partial: PartialVersion): SemVer {
|
|
131
|
+
return {
|
|
132
|
+
major: partial.major ?? 0,
|
|
133
|
+
minor: partial.minor ?? 0,
|
|
134
|
+
patch: partial.patch ?? 0,
|
|
135
|
+
prerelease: partial.prerelease,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function nextWildcard(partial: PartialVersion): SemVer | null {
|
|
140
|
+
if (partial.major === null) return null;
|
|
141
|
+
if (partial.minor === null) return { major: partial.major + 1, minor: 0, patch: 0, prerelease: [] };
|
|
142
|
+
if (partial.patch === null) return { major: partial.major, minor: partial.minor + 1, patch: 0, prerelease: [] };
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function lowerBound(version: SemVer, inclusive: boolean): Predicate {
|
|
147
|
+
return (candidate) => (inclusive ? compare(candidate, version) >= 0 : compare(candidate, version) > 0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function upperBound(version: SemVer, inclusive: boolean): Predicate {
|
|
151
|
+
return (candidate) => (inclusive ? compare(candidate, version) <= 0 : compare(candidate, version) < 0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function core(version: SemVer): string {
|
|
155
|
+
return `${version.major}.${version.minor}.${version.patch}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function predicatesForToken(token: string): { predicates: Predicate[]; prereleaseCore: string | null } | null {
|
|
159
|
+
const match = /^(<=|>=|<|>|=|~|\^)?(.+)$/u.exec(token);
|
|
160
|
+
if (match === null) return null;
|
|
161
|
+
const operator = match[1] ?? "";
|
|
162
|
+
const partial = parsePartial(match[2] ?? "");
|
|
163
|
+
if (partial === null) return null;
|
|
164
|
+
const base = floor(partial);
|
|
165
|
+
const wildcardUpper = nextWildcard(partial);
|
|
166
|
+
const prereleaseCore = partial.prerelease.length > 0 ? core(base) : null;
|
|
167
|
+
|
|
168
|
+
if (operator === "" || operator === "=") {
|
|
169
|
+
if (partial.major === null) return { predicates: [], prereleaseCore };
|
|
170
|
+
if (wildcardUpper !== null) {
|
|
171
|
+
return { predicates: [lowerBound(base, true), upperBound(wildcardUpper, false)], prereleaseCore };
|
|
172
|
+
}
|
|
173
|
+
return { predicates: [(candidate) => compare(candidate, base) === 0], prereleaseCore };
|
|
174
|
+
}
|
|
175
|
+
if (operator === ">=") return { predicates: [lowerBound(base, true)], prereleaseCore };
|
|
176
|
+
if (operator === "<") return { predicates: [upperBound(base, false)], prereleaseCore };
|
|
177
|
+
if (operator === ">") {
|
|
178
|
+
return {
|
|
179
|
+
predicates: [wildcardUpper === null ? lowerBound(base, false) : lowerBound(wildcardUpper, true)],
|
|
180
|
+
prereleaseCore,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (operator === "<=") {
|
|
184
|
+
return {
|
|
185
|
+
predicates: [wildcardUpper === null ? upperBound(base, true) : upperBound(wildcardUpper, false)],
|
|
186
|
+
prereleaseCore,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (partial.major === null) return { predicates: [], prereleaseCore };
|
|
190
|
+
if (operator === "~") {
|
|
191
|
+
const upper =
|
|
192
|
+
partial.minor === null
|
|
193
|
+
? { major: partial.major + 1, minor: 0, patch: 0, prerelease: [] }
|
|
194
|
+
: { major: partial.major, minor: partial.minor + 1, patch: 0, prerelease: [] };
|
|
195
|
+
return { predicates: [lowerBound(base, true), upperBound(upper, false)], prereleaseCore };
|
|
196
|
+
}
|
|
197
|
+
const upper =
|
|
198
|
+
partial.major > 0
|
|
199
|
+
? { major: partial.major + 1, minor: 0, patch: 0, prerelease: [] }
|
|
200
|
+
: partial.minor === null
|
|
201
|
+
? { major: 1, minor: 0, patch: 0, prerelease: [] }
|
|
202
|
+
: (partial.minor ?? 0) > 0
|
|
203
|
+
? { major: 0, minor: (partial.minor ?? 0) + 1, patch: 0, prerelease: [] }
|
|
204
|
+
: partial.patch === null
|
|
205
|
+
? { major: 0, minor: 1, patch: 0, prerelease: [] }
|
|
206
|
+
: { major: 0, minor: 0, patch: (partial.patch ?? 0) + 1, prerelease: [] };
|
|
207
|
+
return { predicates: [lowerBound(base, true), upperBound(upper, false)], prereleaseCore };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function predicatesForSet(raw: string): { predicates: Predicate[]; prereleaseCores: ReadonlySet<string> } | null {
|
|
211
|
+
const hyphen = /^(\S+)\s+-\s+(\S+)$/u.exec(raw);
|
|
212
|
+
if (hyphen !== null) {
|
|
213
|
+
const left = parsePartial(hyphen[1] ?? "");
|
|
214
|
+
const right = parsePartial(hyphen[2] ?? "");
|
|
215
|
+
if (left === null || right === null || left.major === null || right.major === null) return null;
|
|
216
|
+
const rightUpper = nextWildcard(right);
|
|
217
|
+
return {
|
|
218
|
+
predicates: [
|
|
219
|
+
lowerBound(floor(left), true),
|
|
220
|
+
rightUpper === null ? upperBound(floor(right), true) : upperBound(rightUpper, false),
|
|
221
|
+
],
|
|
222
|
+
prereleaseCores: new Set(
|
|
223
|
+
[
|
|
224
|
+
left.prerelease.length > 0 ? core(floor(left)) : null,
|
|
225
|
+
right.prerelease.length > 0 ? core(floor(right)) : null,
|
|
226
|
+
].filter((entry): entry is string => entry !== null),
|
|
227
|
+
),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const normalized = raw.replace(/(<=|>=|<|>|=|~|\^)\s+/gu, "$1");
|
|
231
|
+
const tokens = normalized.split(/\s+/u).filter((entry) => entry.length > 0);
|
|
232
|
+
if (tokens.length === 0) return null;
|
|
233
|
+
const predicates: Predicate[] = [];
|
|
234
|
+
const prereleaseCores = new Set<string>();
|
|
235
|
+
for (const token of tokens) {
|
|
236
|
+
const parsed = predicatesForToken(token);
|
|
237
|
+
if (parsed === null) return null;
|
|
238
|
+
predicates.push(...parsed.predicates);
|
|
239
|
+
if (parsed.prereleaseCore !== null) prereleaseCores.add(parsed.prereleaseCore);
|
|
240
|
+
}
|
|
241
|
+
return { predicates, prereleaseCores };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function parseRange(
|
|
245
|
+
range: string,
|
|
246
|
+
): ReadonlyArray<{ predicates: Predicate[]; prereleaseCores: ReadonlySet<string> }> | null {
|
|
247
|
+
const trimmed = range.trim();
|
|
248
|
+
if (trimmed.length === 0 || trimmed.length > MAX_RANGE_CHARS) return null;
|
|
249
|
+
const sets = trimmed.split("||");
|
|
250
|
+
if (sets.some((set) => set.trim().length === 0)) return null;
|
|
251
|
+
const parsed = sets.map((set) => predicatesForSet(set.trim()));
|
|
252
|
+
return parsed.every((set) => set !== null) ? (parsed as Array<NonNullable<(typeof parsed)[number]>>) : null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function isValidSemVerRange(range: string): boolean {
|
|
256
|
+
return parseRange(range) !== null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function satisfiesSemVerRange(version: string, range: string): boolean {
|
|
260
|
+
const candidate = parseSemVer(version);
|
|
261
|
+
const sets = parseRange(range);
|
|
262
|
+
if (candidate === null || sets === null) return false;
|
|
263
|
+
return sets.some((set) => {
|
|
264
|
+
if (candidate.prerelease.length > 0 && !set.prereleaseCores.has(core(candidate))) return false;
|
|
265
|
+
return set.predicates.every((predicate) => predicate(candidate));
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface ClioCompatibilityEvaluation {
|
|
270
|
+
rangeValid: boolean;
|
|
271
|
+
satisfied: boolean;
|
|
272
|
+
runningVersion: string;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function evaluateClioCompatibility(
|
|
276
|
+
range: string,
|
|
277
|
+
runningVersion = getVersionInfo().clio,
|
|
278
|
+
): ClioCompatibilityEvaluation {
|
|
279
|
+
const rangeValid = isValidSemVerRange(range);
|
|
280
|
+
return {
|
|
281
|
+
rangeValid,
|
|
282
|
+
satisfied: rangeValid && satisfiesSemVerRange(runningVersion, range),
|
|
283
|
+
runningVersion,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, lstatSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
4
|
+
import { evaluateClioCompatibility } from "./compatibility.js";
|
|
4
5
|
import type {
|
|
5
6
|
ClioExtensionManifest,
|
|
6
7
|
ExtensionCandidate,
|
|
@@ -166,9 +167,32 @@ export function parseExtensionManifest(
|
|
|
166
167
|
const resources = normalizeResources(value.resources);
|
|
167
168
|
const tools = stringArray(value.tools);
|
|
168
169
|
const settings = stringArray(value.settings);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
let compatibility: ClioExtensionManifest["compatibility"];
|
|
171
|
+
if (value.compatibility !== undefined) {
|
|
172
|
+
if (!isRecord(value.compatibility)) {
|
|
173
|
+
diagnostics.push({ type: "error", message: "compatibility must be an object", path: manifestPath });
|
|
174
|
+
} else if ("clio" in value.compatibility) {
|
|
175
|
+
const range = trimString(value.compatibility.clio);
|
|
176
|
+
if (range === undefined) {
|
|
177
|
+
diagnostics.push({
|
|
178
|
+
type: "error",
|
|
179
|
+
message: "compatibility.clio must be a non-empty semver range",
|
|
180
|
+
path: manifestPath,
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
const evaluation = evaluateClioCompatibility(range);
|
|
184
|
+
if (!evaluation.rangeValid) {
|
|
185
|
+
diagnostics.push({
|
|
186
|
+
type: "error",
|
|
187
|
+
message: `extension ${id ?? "<unknown>"} declares malformed compatibility.clio range '${range}'`,
|
|
188
|
+
path: manifestPath,
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
compatibility = { clio: range };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
172
196
|
if (!id || !name || !version || !description || diagnostics.some((diag) => diag.type === "error")) {
|
|
173
197
|
return { diagnostics };
|
|
174
198
|
}
|
|
@@ -183,6 +207,17 @@ export function parseExtensionManifest(
|
|
|
183
207
|
if (tools) manifest.tools = tools;
|
|
184
208
|
if (settings) manifest.settings = settings;
|
|
185
209
|
if (compatibility && Object.keys(compatibility).length > 0) manifest.compatibility = compatibility;
|
|
210
|
+
const clioRange = manifest.compatibility?.clio;
|
|
211
|
+
if (clioRange !== undefined) {
|
|
212
|
+
const evaluation = evaluateClioCompatibility(clioRange);
|
|
213
|
+
if (!evaluation.satisfied) {
|
|
214
|
+
diagnostics.push({
|
|
215
|
+
type: "error",
|
|
216
|
+
message: `extension ${manifest.id} requires Clio '${clioRange}', but running Clio version is '${evaluation.runningVersion}'`,
|
|
217
|
+
path: manifestPath,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
186
221
|
return { manifest, diagnostics };
|
|
187
222
|
}
|
|
188
223
|
|
|
@@ -33,7 +33,7 @@ export function enabledExtensionResourceRoots(
|
|
|
33
33
|
): ExtensionResourceRoot[] {
|
|
34
34
|
const roots: ExtensionResourceRoot[] = [];
|
|
35
35
|
for (const entry of listInstalledExtensions(cwd)) {
|
|
36
|
-
if (!entry.enabled || !entry.effective) continue;
|
|
36
|
+
if (!entry.enabled || !entry.compatible || !entry.effective) continue;
|
|
37
37
|
const rel = entry.resources[kind];
|
|
38
38
|
if (!rel) continue;
|
|
39
39
|
const full = extensionResourcePath(entry.rootPath, rel);
|