@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
|
@@ -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
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
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,
|
|
@@ -39,13 +40,99 @@ function normalizeResources(value: unknown): ExtensionManifestResources {
|
|
|
39
40
|
const out: ExtensionManifestResources = {};
|
|
40
41
|
const skills = trimString(value.skills);
|
|
41
42
|
const prompts = trimString(value.prompts);
|
|
43
|
+
const agents = trimString(value.agents);
|
|
44
|
+
const fleets = trimString(value.fleets);
|
|
42
45
|
const themes = trimString(value.themes);
|
|
43
46
|
if (skills) out.skills = skills;
|
|
44
47
|
if (prompts) out.prompts = prompts;
|
|
48
|
+
if (agents) out.agents = agents;
|
|
49
|
+
if (fleets) out.fleets = fleets;
|
|
45
50
|
if (themes) out.themes = themes;
|
|
46
51
|
return out;
|
|
47
52
|
}
|
|
48
53
|
|
|
54
|
+
function contained(root: string, candidate: string): boolean {
|
|
55
|
+
const relative = path.relative(root, candidate);
|
|
56
|
+
return relative === "" || (!relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function validateResourceTree(
|
|
60
|
+
root: string,
|
|
61
|
+
resourcePath: string,
|
|
62
|
+
kind: string,
|
|
63
|
+
diagnostics: ExtensionDiagnostic[],
|
|
64
|
+
): void {
|
|
65
|
+
if (path.isAbsolute(resourcePath)) {
|
|
66
|
+
diagnostics.push({ type: "error", message: `resources.${kind} must be relative`, path: resourcePath });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const resolvedRoot = path.resolve(root);
|
|
70
|
+
const resolved = path.resolve(resolvedRoot, resourcePath);
|
|
71
|
+
if (!contained(resolvedRoot, resolved) || resolved === resolvedRoot) {
|
|
72
|
+
diagnostics.push({ type: "error", message: `resources.${kind} escapes the extension root`, path: resourcePath });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
let canonicalRoot: string;
|
|
76
|
+
let canonicalResource: string;
|
|
77
|
+
try {
|
|
78
|
+
canonicalRoot = realpathSync(resolvedRoot);
|
|
79
|
+
canonicalResource = realpathSync(resolved);
|
|
80
|
+
if (!lstatSync(resolved).isDirectory()) {
|
|
81
|
+
diagnostics.push({ type: "error", message: `resources.${kind} is not a directory`, path: resolved });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
diagnostics.push({
|
|
86
|
+
type: "error",
|
|
87
|
+
message: `resources.${kind} cannot be resolved: ${error instanceof Error ? error.message : String(error)}`,
|
|
88
|
+
path: resolved,
|
|
89
|
+
});
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (!contained(canonicalRoot, canonicalResource)) {
|
|
93
|
+
diagnostics.push({ type: "error", message: `resources.${kind} escapes through a symbolic link`, path: resolved });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const pending = [resolved];
|
|
98
|
+
while (pending.length > 0) {
|
|
99
|
+
const directory = pending.pop();
|
|
100
|
+
if (!directory) continue;
|
|
101
|
+
for (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
102
|
+
const candidate = path.join(directory, entry.name);
|
|
103
|
+
if (entry.isSymbolicLink()) {
|
|
104
|
+
try {
|
|
105
|
+
if (!contained(canonicalRoot, realpathSync(candidate))) {
|
|
106
|
+
diagnostics.push({
|
|
107
|
+
type: "error",
|
|
108
|
+
message: `resources.${kind} contains an escaping symbolic link`,
|
|
109
|
+
path: candidate,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
diagnostics.push({
|
|
114
|
+
type: "error",
|
|
115
|
+
message: `resources.${kind} contains an unresolved symbolic link: ${error instanceof Error ? error.message : String(error)}`,
|
|
116
|
+
path: candidate,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
} else if (entry.isDirectory()) {
|
|
120
|
+
pending.push(candidate);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function validateResourceRoots(
|
|
127
|
+
root: string,
|
|
128
|
+
resources: ExtensionManifestResources,
|
|
129
|
+
diagnostics: ExtensionDiagnostic[],
|
|
130
|
+
): void {
|
|
131
|
+
for (const [kind, resourcePath] of Object.entries(resources)) {
|
|
132
|
+
if (resourcePath) validateResourceTree(root, resourcePath, kind, diagnostics);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
49
136
|
function stringArray(value: unknown): string[] | undefined {
|
|
50
137
|
if (!Array.isArray(value)) return undefined;
|
|
51
138
|
const out = value.map((entry) => trimString(entry)).filter((entry): entry is string => entry !== undefined);
|
|
@@ -80,9 +167,32 @@ export function parseExtensionManifest(
|
|
|
80
167
|
const resources = normalizeResources(value.resources);
|
|
81
168
|
const tools = stringArray(value.tools);
|
|
82
169
|
const settings = stringArray(value.settings);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
+
}
|
|
86
196
|
if (!id || !name || !version || !description || diagnostics.some((diag) => diag.type === "error")) {
|
|
87
197
|
return { diagnostics };
|
|
88
198
|
}
|
|
@@ -97,6 +207,17 @@ export function parseExtensionManifest(
|
|
|
97
207
|
if (tools) manifest.tools = tools;
|
|
98
208
|
if (settings) manifest.settings = settings;
|
|
99
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
|
+
}
|
|
100
221
|
return { manifest, diagnostics };
|
|
101
222
|
}
|
|
102
223
|
|
|
@@ -123,6 +244,7 @@ export function loadManifestFromRoot(root: string): ExtensionCandidate {
|
|
|
123
244
|
}
|
|
124
245
|
try {
|
|
125
246
|
const parsed = parseExtensionManifest(readJsonOrYaml(manifestPath), manifestPath);
|
|
247
|
+
if (parsed.manifest) validateResourceRoots(root, parsed.manifest.resources, parsed.diagnostics);
|
|
126
248
|
return {
|
|
127
249
|
path: root,
|
|
128
250
|
manifestPath,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { lstatSync, realpathSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { listInstalledExtensions } from "./state.js";
|
|
4
4
|
import type { ExtensionResourceKind, ExtensionResourceRoot } from "./types.js";
|
|
@@ -7,8 +7,24 @@ export function extensionResourcePath(rootPath: string, resourcePath: string): s
|
|
|
7
7
|
const root = path.resolve(rootPath);
|
|
8
8
|
const full = path.resolve(root, resourcePath);
|
|
9
9
|
const relative = path.relative(root, full);
|
|
10
|
-
if (relative === "" ||
|
|
11
|
-
|
|
10
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
const rootReal = realpathSync(root);
|
|
14
|
+
const fullReal = realpathSync(full);
|
|
15
|
+
const canonicalRelative = path.relative(rootReal, fullReal);
|
|
16
|
+
if (
|
|
17
|
+
canonicalRelative === ".." ||
|
|
18
|
+
canonicalRelative.startsWith(`..${path.sep}`) ||
|
|
19
|
+
path.isAbsolute(canonicalRelative)
|
|
20
|
+
) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (!lstatSync(full).isDirectory()) return null;
|
|
24
|
+
return full;
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
12
28
|
}
|
|
13
29
|
|
|
14
30
|
export function enabledExtensionResourceRoots(
|
|
@@ -17,20 +33,16 @@ export function enabledExtensionResourceRoots(
|
|
|
17
33
|
): ExtensionResourceRoot[] {
|
|
18
34
|
const roots: ExtensionResourceRoot[] = [];
|
|
19
35
|
for (const entry of listInstalledExtensions(cwd)) {
|
|
20
|
-
if (!entry.enabled || !entry.effective) continue;
|
|
36
|
+
if (!entry.enabled || !entry.compatible || !entry.effective) continue;
|
|
21
37
|
const rel = entry.resources[kind];
|
|
22
38
|
if (!rel) continue;
|
|
23
39
|
const full = extensionResourcePath(entry.rootPath, rel);
|
|
24
40
|
if (!full) continue;
|
|
25
|
-
try {
|
|
26
|
-
if (!statSync(full).isDirectory()) continue;
|
|
27
|
-
} catch {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
41
|
roots.push({
|
|
31
42
|
id: entry.id,
|
|
32
43
|
scope: entry.scope,
|
|
33
44
|
path: full,
|
|
45
|
+
rootPath: entry.rootPath,
|
|
34
46
|
source: `extension:${entry.scope}:${entry.id}`,
|
|
35
47
|
});
|
|
36
48
|
}
|
|
@@ -2,6 +2,7 @@ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, r
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { safeResourceWrite } from "../../core/safe-resource-write.js";
|
|
4
4
|
import { clioConfigDir } from "../../core/xdg.js";
|
|
5
|
+
import { evaluateClioCompatibility } from "./compatibility.js";
|
|
5
6
|
import { isRecord, loadManifestFromRoot, trimString } from "./discovery.js";
|
|
6
7
|
import type {
|
|
7
8
|
ExtensionInstallOptions,
|
|
@@ -63,6 +64,8 @@ function installedFromRoot(root: string, scope: ExtensionScope, state: Extension
|
|
|
63
64
|
const candidate = loadManifestFromRoot(root);
|
|
64
65
|
const manifest = candidate.manifest;
|
|
65
66
|
if (!manifest || !candidate.manifestPath) return null;
|
|
67
|
+
const clioRange = manifest.compatibility?.clio;
|
|
68
|
+
const compatible = clioRange === undefined || evaluateClioCompatibility(clioRange).satisfied;
|
|
66
69
|
return {
|
|
67
70
|
id: manifest.id,
|
|
68
71
|
name: manifest.name,
|
|
@@ -72,6 +75,7 @@ function installedFromRoot(root: string, scope: ExtensionScope, state: Extension
|
|
|
72
75
|
rootPath: root,
|
|
73
76
|
manifestPath: candidate.manifestPath,
|
|
74
77
|
enabled: !state.disabled.includes(manifest.id),
|
|
78
|
+
compatible,
|
|
75
79
|
effective: false,
|
|
76
80
|
resources: manifest.resources,
|
|
77
81
|
diagnostics: candidate.diagnostics,
|
|
@@ -102,13 +106,18 @@ export function listInstalledExtensions(cwd = process.cwd(), options: ExtensionL
|
|
|
102
106
|
byId.set(entry.id, list);
|
|
103
107
|
}
|
|
104
108
|
for (const group of byId.values()) {
|
|
105
|
-
const winner =
|
|
109
|
+
const winner = group
|
|
110
|
+
.filter((entry) => entry.compatible)
|
|
111
|
+
.sort((a, b) => scopeRank(a.scope) - scopeRank(b.scope))
|
|
112
|
+
.at(-1);
|
|
106
113
|
for (const entry of group) {
|
|
107
114
|
entry.effective = entry === winner;
|
|
108
|
-
if (!entry.effective && winner) entry.overriddenBy = winner.scope;
|
|
115
|
+
if (entry.compatible && !entry.effective && winner) entry.overriddenBy = winner.scope;
|
|
109
116
|
}
|
|
110
117
|
}
|
|
111
|
-
|
|
118
|
+
// Incompatible packages remain visible by default so the load refusal and
|
|
119
|
+
// its version diagnostic cannot disappear with the resources it suppresses.
|
|
120
|
+
const all = options.all === true ? entries : entries.filter((entry) => entry.effective || !entry.compatible);
|
|
112
121
|
return all.sort((a, b) => {
|
|
113
122
|
const id = a.id.localeCompare(b.id);
|
|
114
123
|
if (id !== 0) return id;
|
|
@@ -129,7 +138,7 @@ export function installExtension(sourcePath: string, options: ExtensionInstallOp
|
|
|
129
138
|
const cwd = options.cwd ?? process.cwd();
|
|
130
139
|
const source = path.resolve(sourcePath);
|
|
131
140
|
const candidate = loadManifestFromRoot(source);
|
|
132
|
-
if (!candidate.manifest) return { diagnostics: candidate.diagnostics };
|
|
141
|
+
if (!candidate.manifest || !candidate.valid) return { diagnostics: candidate.diagnostics };
|
|
133
142
|
const targetRoot = path.join(extensionBaseDir(scope, cwd), candidate.manifest.id);
|
|
134
143
|
if (existsSync(targetRoot)) {
|
|
135
144
|
if (!options.force) {
|
|
@@ -152,7 +161,11 @@ export function installExtension(sourcePath: string, options: ExtensionInstallOp
|
|
|
152
161
|
rmSync(backupRoot, { recursive: true, force: true });
|
|
153
162
|
cpSync(source, stagingRoot, {
|
|
154
163
|
recursive: true,
|
|
155
|
-
|
|
164
|
+
// `state.json` beside the installed extension directories belongs to
|
|
165
|
+
// Clio's extension manager. An extension may legitimately ship nested
|
|
166
|
+
// resources with the same basename (for example a project-state JSON
|
|
167
|
+
// template), so exclude only a bookkeeping file at the package root.
|
|
168
|
+
filter: (src) => path.relative(source, src) !== "state.json",
|
|
156
169
|
});
|
|
157
170
|
if (existsSync(targetRoot)) {
|
|
158
171
|
renameSync(targetRoot, backupRoot);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export type ExtensionScope = "user" | "project";
|
|
2
|
-
export type ExtensionResourceKind = "skills" | "prompts" | "themes";
|
|
2
|
+
export type ExtensionResourceKind = "skills" | "prompts" | "agents" | "fleets" | "themes";
|
|
3
3
|
|
|
4
4
|
export interface ExtensionManifestResources {
|
|
5
5
|
skills?: string;
|
|
6
6
|
prompts?: string;
|
|
7
|
+
agents?: string;
|
|
8
|
+
fleets?: string;
|
|
7
9
|
themes?: string;
|
|
8
10
|
}
|
|
9
11
|
|
|
@@ -34,6 +36,8 @@ export interface InstalledExtension {
|
|
|
34
36
|
rootPath: string;
|
|
35
37
|
manifestPath: string;
|
|
36
38
|
enabled: boolean;
|
|
39
|
+
/** Whether this package admits the running Clio version. */
|
|
40
|
+
compatible: boolean;
|
|
37
41
|
effective: boolean;
|
|
38
42
|
resources: ExtensionManifestResources;
|
|
39
43
|
overriddenBy?: ExtensionScope;
|
|
@@ -52,6 +56,7 @@ export interface ExtensionResourceRoot {
|
|
|
52
56
|
id: string;
|
|
53
57
|
scope: ExtensionScope;
|
|
54
58
|
path: string;
|
|
59
|
+
rootPath: string;
|
|
55
60
|
source: string;
|
|
56
61
|
}
|
|
57
62
|
|