Merge remote-tracking branch 'refs/remotes/origin/develop' into work/T-468-remove-local-workspace-authority
This commit is contained in:
Generated
+8
-654
@@ -2,15 +2,6 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "addr2line"
|
|
||||||
version = "0.26.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59317f77929f0e679d39364702289274de2f0f0b22cbf50b2b8cff2169a0b27a"
|
|
||||||
dependencies = [
|
|
||||||
"gimli",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler2"
|
name = "adler2"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -177,12 +168,6 @@ version = "1.0.102"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arbitrary"
|
|
||||||
version = "1.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arc-swap"
|
name = "arc-swap"
|
||||||
version = "1.9.1"
|
version = "1.9.1"
|
||||||
@@ -476,9 +461,6 @@ name = "bumpalo"
|
|||||||
version = "3.20.2"
|
version = "3.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||||
dependencies = [
|
|
||||||
"allocator-api2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecount"
|
name = "bytecount"
|
||||||
@@ -662,15 +644,6 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cobs"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -765,15 +738,6 @@ version = "0.8.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpp_demangle"
|
|
||||||
version = "0.4.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@@ -792,148 +756,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-assembler-x64"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0bc293b86236abcc45f2f72e2d18e2bd636f2a08b75eb286bae31e71e1430c91"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-assembler-x64-meta",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-assembler-x64-meta"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b954c826eddaf1b001402cb8aecf1764c6f6d637ba69fb9e3311f1ebac965be6"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-srcgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-bforest"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4053fa2575ef4a5c35d2708533df2200400ae979226cea9cc92a578b811bd4e7"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-entity",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-bitset"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d216663191014aa63e1d2cffd058e609eaf207646d40b739d88250f65b2c4f69"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-codegen"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a5e7e7aad6a425a51da1ad7ab9e5d280ea97eb7c7c4545fafb567915a75aadb"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"cranelift-assembler-x64",
|
|
||||||
"cranelift-bforest",
|
|
||||||
"cranelift-bitset",
|
|
||||||
"cranelift-codegen-meta",
|
|
||||||
"cranelift-codegen-shared",
|
|
||||||
"cranelift-control",
|
|
||||||
"cranelift-entity",
|
|
||||||
"cranelift-isle",
|
|
||||||
"gimli",
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"libm",
|
|
||||||
"log",
|
|
||||||
"pulley-interpreter",
|
|
||||||
"regalloc2",
|
|
||||||
"rustc-hash",
|
|
||||||
"serde",
|
|
||||||
"smallvec",
|
|
||||||
"target-lexicon",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-codegen-meta"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c421d80a9a85f806cb02a2983b5b5368a335c319795b1f1b4b771a24479af5b0"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-assembler-x64-meta",
|
|
||||||
"cranelift-codegen-shared",
|
|
||||||
"cranelift-srcgen",
|
|
||||||
"heck",
|
|
||||||
"pulley-interpreter",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-codegen-shared"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "78fdb83ab012d0ee6a44ced7ca8788a444f17cf821c62f95d6ef87c9f0262518"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-control"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b75adc6eb7bb4ac6365106afb6cac4f12fe1ddfa02ddc9fd7015ca1469b471b"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-entity"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "668e56db75a54816cbdd7c7b7bfc558b08bf7b2cda9d0846491517e92f3b393b"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-bitset",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-frontend"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c63892dc1cc3ae48680183fa66997f60ffe7f1e200c8d390f8ee66edff4aef5a"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-codegen",
|
|
||||||
"log",
|
|
||||||
"smallvec",
|
|
||||||
"target-lexicon",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-isle"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94eaf429c32a12715429c7c6ddfdd43c170f4cdd7e97bfa507bd68a652091087"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-native"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cd77674904ae9be11c1e1efdba54788b59f3d6658d747b97534bfbba2909aacc"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-codegen",
|
|
||||||
"libc",
|
|
||||||
"target-lexicon",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cranelift-srcgen"
|
|
||||||
version = "0.132.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cba7c0ff5941842c36653da155580ce41e675c204a67ac1b4e1c478a9347bbb7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1374,18 +1196,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-io"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-io"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.35"
|
version = "0.8.35"
|
||||||
@@ -1788,18 +1598,6 @@ dependencies = [
|
|||||||
"polyval",
|
"polyval",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.33.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0bf7f043f89559805f8c7cacc432749b2fa0d0a0a9ee46ce47164ed5ba7f126c"
|
|
||||||
dependencies = [
|
|
||||||
"fnv",
|
|
||||||
"hashbrown 0.16.1",
|
|
||||||
"indexmap",
|
|
||||||
"stable_deref_trait",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@@ -1922,11 +1720,6 @@ name = "hashbrown"
|
|||||||
version = "0.17.1"
|
version = "0.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
dependencies = [
|
|
||||||
"foldhash 0.2.0",
|
|
||||||
"serde",
|
|
||||||
"serde_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashlink"
|
name = "hashlink"
|
||||||
@@ -2614,15 +2407,6 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mach2"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "manifest"
|
name = "manifest"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -2713,15 +2497,6 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memfd"
|
|
||||||
version = "0.6.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227"
|
|
||||||
dependencies = [
|
|
||||||
"rustix 1.1.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memmap2"
|
name = "memmap2"
|
||||||
version = "0.9.10"
|
version = "0.9.10"
|
||||||
@@ -3003,18 +2778,6 @@ version = "0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
|
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.39.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b"
|
|
||||||
dependencies = [
|
|
||||||
"crc32fast",
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"indexmap",
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oid-registry"
|
name = "oid-registry"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -3410,18 +3173,6 @@ version = "1.13.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "postcard"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
|
||||||
dependencies = [
|
|
||||||
"cobs",
|
|
||||||
"embedded-io 0.4.0",
|
|
||||||
"embedded-io 0.6.1",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "postscript"
|
name = "postscript"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
@@ -3524,29 +3275,6 @@ dependencies = [
|
|||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pulley-interpreter"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d9880c1985ccccaed3646b0ef793dc39a4b117403ed4afc6fa3ef6027c5200f"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-bitset",
|
|
||||||
"log",
|
|
||||||
"pulley-macros",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pulley-macros"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee249346855ad102580e474da5463f86f8a7d449e6d49e00fefb304e448e2983"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.117",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quinn"
|
name = "quinn"
|
||||||
version = "0.11.9"
|
version = "0.11.9"
|
||||||
@@ -3824,20 +3552,6 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regalloc2"
|
|
||||||
version = "0.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "de2c52737737f8609e94f975dee22854a2d5c125772d4b1cf292120f4d45c186"
|
|
||||||
dependencies = [
|
|
||||||
"allocator-api2",
|
|
||||||
"bumpalo",
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"log",
|
|
||||||
"rustc-hash",
|
|
||||||
"smallvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.3"
|
version = "1.12.3"
|
||||||
@@ -3971,12 +3685,6 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-demangle"
|
|
||||||
version = "0.1.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.1.2"
|
version = "2.1.2"
|
||||||
@@ -4216,10 +3924,6 @@ name = "semver"
|
|||||||
version = "1.0.27"
|
version = "1.0.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
@@ -4536,9 +4240,6 @@ name = "smallvec"
|
|||||||
version = "1.15.1"
|
version = "1.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
@@ -4771,12 +4472,6 @@ dependencies = [
|
|||||||
"xattr",
|
"xattr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "target-lexicon"
|
|
||||||
version = "0.13.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-triple"
|
name = "target-triple"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -5702,27 +5397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"leb128fmt",
|
"leb128fmt",
|
||||||
"wasmparser 0.244.0",
|
"wasmparser",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-encoder"
|
|
||||||
version = "0.248.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac92cf547bc18d27ecc521015c08c353b4f18b84ab388bb6d1b6b682c620d9b6"
|
|
||||||
dependencies = [
|
|
||||||
"leb128fmt",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-encoder"
|
|
||||||
version = "0.252.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f"
|
|
||||||
dependencies = [
|
|
||||||
"leb128fmt",
|
|
||||||
"wasmparser 0.252.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5733,8 +5408,8 @@ checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"wasm-encoder 0.244.0",
|
"wasm-encoder",
|
||||||
"wasmparser 0.244.0",
|
"wasmparser",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5762,286 +5437,6 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmparser"
|
|
||||||
version = "0.248.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.11.0",
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"indexmap",
|
|
||||||
"semver",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmparser"
|
|
||||||
version = "0.252.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.11.0",
|
|
||||||
"indexmap",
|
|
||||||
"semver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmprinter"
|
|
||||||
version = "0.248.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "30b264a5410b008d4d199a92bf536eae703cbd614482fc1ec53831cf19e1c183"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"termcolor",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c7ce9aa2c67f75fadcfdc6aa9097d03e7c39485dfe316f2ed6a7c0fd186c527"
|
|
||||||
dependencies = [
|
|
||||||
"addr2line",
|
|
||||||
"async-trait",
|
|
||||||
"bitflags 2.11.0",
|
|
||||||
"bumpalo",
|
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
|
||||||
"encoding_rs",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"mach2",
|
|
||||||
"memfd",
|
|
||||||
"object",
|
|
||||||
"once_cell",
|
|
||||||
"postcard",
|
|
||||||
"pulley-interpreter",
|
|
||||||
"rustix 1.1.4",
|
|
||||||
"semver",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"smallvec",
|
|
||||||
"target-lexicon",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
"wasmtime-environ",
|
|
||||||
"wasmtime-internal-component-macro",
|
|
||||||
"wasmtime-internal-component-util",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
"wasmtime-internal-cranelift",
|
|
||||||
"wasmtime-internal-fiber",
|
|
||||||
"wasmtime-internal-jit-debug",
|
|
||||||
"wasmtime-internal-jit-icache-coherence",
|
|
||||||
"wasmtime-internal-unwinder",
|
|
||||||
"wasmtime-internal-versioned-export-macros",
|
|
||||||
"wasmtime-internal-winch",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-environ"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c8fb157bd1fbf689ac89d570433a700db6f33bdfcb5ffc30e3f1c49e4c70de71"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"cpp_demangle",
|
|
||||||
"cranelift-bforest",
|
|
||||||
"cranelift-bitset",
|
|
||||||
"cranelift-entity",
|
|
||||||
"gimli",
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"indexmap",
|
|
||||||
"log",
|
|
||||||
"object",
|
|
||||||
"postcard",
|
|
||||||
"rustc-demangle",
|
|
||||||
"semver",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"sha2 0.10.9",
|
|
||||||
"smallvec",
|
|
||||||
"target-lexicon",
|
|
||||||
"wasm-encoder 0.248.0",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
"wasmprinter",
|
|
||||||
"wasmtime-internal-component-util",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-component-macro"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b96c17f35fae2ab574667aba0c58fd56349a6f788ac42541a2e543116d5cfb91"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.117",
|
|
||||||
"wasmtime-internal-component-util",
|
|
||||||
"wasmtime-internal-wit-bindgen",
|
|
||||||
"wit-parser 0.248.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-component-util"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d2eeb9b53222859e6f5dc73d2ccfb33254d672469cac11b693a71912e2f3817"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-core"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4a1deaf6bc3430abd7497b00c64f06ca2b97ca0fe41af87836446ca30949965c"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"libm",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-cranelift"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b845f83b5b04b11bc48329b53eb4fa8cf9f28a43c71ed8e1203f68ffa9806d1b"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cranelift-codegen",
|
|
||||||
"cranelift-control",
|
|
||||||
"cranelift-entity",
|
|
||||||
"cranelift-frontend",
|
|
||||||
"cranelift-native",
|
|
||||||
"gimli",
|
|
||||||
"itertools",
|
|
||||||
"log",
|
|
||||||
"object",
|
|
||||||
"pulley-interpreter",
|
|
||||||
"smallvec",
|
|
||||||
"target-lexicon",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
"wasmtime-environ",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
"wasmtime-internal-unwinder",
|
|
||||||
"wasmtime-internal-versioned-export-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-fiber"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e10c8466f72965ae85c250f90aaa7992c089a2f8502009bd0d2c9e7d6409174a"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"rustix 1.1.4",
|
|
||||||
"wasmtime-environ",
|
|
||||||
"wasmtime-internal-versioned-export-macros",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-jit-debug"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d3adfecf5621b14d8f8871f4cb4ed9f844197b1ddefc702ef4c859552cd9551"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"wasmtime-internal-versioned-export-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-jit-icache-coherence"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08d3c1e9fb618ec45c9b3477ea683cd37bee427273d7b13bba5c66a1caaf1dd6"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
"windows-sys 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-unwinder"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7aa91132b81f1e172ec7e7c3c114ac34209ee6b3524b3a8d6943af99803f66c5"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cranelift-codegen",
|
|
||||||
"log",
|
|
||||||
"object",
|
|
||||||
"wasmtime-environ",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-versioned-export-macros"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6ea811ffe23f597cc7708327ea25d9eb018dcf760ffe15ccb7d0b27ad635de61"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.117",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-winch"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "828b66175c54a0d00b4c1c1c76658d8aa73aeb9fa3553575c5eee56d40f2eb18"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-codegen",
|
|
||||||
"gimli",
|
|
||||||
"log",
|
|
||||||
"object",
|
|
||||||
"target-lexicon",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
"wasmtime-environ",
|
|
||||||
"wasmtime-internal-cranelift",
|
|
||||||
"winch-codegen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasmtime-internal-wit-bindgen"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ae00896ad9bef1b3ca6401ae9a841daa6f357dd91541b6baf87082946d1bde1"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"bitflags 2.11.0",
|
|
||||||
"heck",
|
|
||||||
"indexmap",
|
|
||||||
"wit-parser 0.248.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wast"
|
|
||||||
version = "252.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "942a3449d6a593fccc111a6241c8df52bda168af30e40bf9580d4394d7374c65"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"leb128fmt",
|
|
||||||
"memchr",
|
|
||||||
"unicode-width",
|
|
||||||
"wasm-encoder 0.252.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wat"
|
|
||||||
version = "1.252.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c72a4ba7088f7bac94cf516e49882bdf97068904a563768cf249efc839ec42cb"
|
|
||||||
dependencies = [
|
|
||||||
"wast",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.94"
|
version = "0.3.94"
|
||||||
@@ -6266,25 +5661,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winch-codegen"
|
|
||||||
version = "45.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "89c09acfdfa281b3340e1e94ef3cf6618d69eab975280f881e154c29f49419c1"
|
|
||||||
dependencies = [
|
|
||||||
"cranelift-assembler-x64",
|
|
||||||
"cranelift-codegen",
|
|
||||||
"gimli",
|
|
||||||
"regalloc2",
|
|
||||||
"smallvec",
|
|
||||||
"target-lexicon",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
"wasmtime-environ",
|
|
||||||
"wasmtime-internal-core",
|
|
||||||
"wasmtime-internal-cranelift",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.62.2"
|
version = "0.62.2"
|
||||||
@@ -6548,7 +5924,7 @@ checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"heck",
|
"heck",
|
||||||
"wit-parser 0.244.0",
|
"wit-parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6595,10 +5971,10 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"wasm-encoder 0.244.0",
|
"wasm-encoder",
|
||||||
"wasm-metadata",
|
"wasm-metadata",
|
||||||
"wasmparser 0.244.0",
|
"wasmparser",
|
||||||
"wit-parser 0.244.0",
|
"wit-parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6616,26 +5992,7 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
"wasmparser 0.244.0",
|
"wasmparser",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wit-parser"
|
|
||||||
version = "0.248.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "247ad505da2915a082fe13204c5ba8788425aea1de54f43b284818cf82637856"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"hashbrown 0.17.1",
|
|
||||||
"id-arena",
|
|
||||||
"indexmap",
|
|
||||||
"log",
|
|
||||||
"semver",
|
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
|
||||||
"serde_json",
|
|
||||||
"unicode-xid",
|
|
||||||
"wasmparser 0.248.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6698,11 +6055,8 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"tungstenite 0.28.0",
|
"tungstenite 0.28.0",
|
||||||
"uuid",
|
"uuid",
|
||||||
"wasmtime",
|
|
||||||
"wat",
|
|
||||||
"workdir",
|
"workdir",
|
||||||
"workspace-api",
|
"workspace-api",
|
||||||
"yoi-plugin-pdk",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::defaults;
|
use crate::defaults;
|
||||||
use crate::model::{AuthRef, ModelManifest, ReasoningControl};
|
use crate::model::{AuthRef, ModelManifest, ReasoningControl};
|
||||||
use crate::plugin::PluginConfig;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CompactionConfig, EngineManifest, FeatureConfig, FeatureFlagConfig, FileUploadLimits,
|
CompactionConfig, EngineManifest, FeatureConfig, FeatureFlagConfig, FileUploadLimits,
|
||||||
McpConfig, McpEnvValue, McpStdioCwdPolicy, MemoryConsolidationProfileConfig,
|
McpConfig, McpEnvValue, McpStdioCwdPolicy, MemoryConsolidationProfileConfig,
|
||||||
@@ -55,10 +54,6 @@ pub struct WorkerManifestConfig {
|
|||||||
/// disabled after cascade merge.
|
/// disabled after cascade merge.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub feature: FeatureConfigPartial,
|
pub feature: FeatureConfigPartial,
|
||||||
/// Explicit plugin package enablement entries. Discovery/resolution is a
|
|
||||||
/// separate step and does not run during config merge.
|
|
||||||
#[serde(default)]
|
|
||||||
pub plugins: PluginConfig,
|
|
||||||
/// Explicit Model Context Protocol provider declarations. Config parsing
|
/// Explicit Model Context Protocol provider declarations. Config parsing
|
||||||
/// never starts a local MCP subprocess.
|
/// never starts a local MCP subprocess.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -74,6 +69,7 @@ pub struct WorkerManifestConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct FeatureConfigPartial {
|
pub struct FeatureConfigPartial {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub task: Option<FeatureFlagConfigPartial>,
|
pub task: Option<FeatureFlagConfigPartial>,
|
||||||
@@ -101,8 +97,6 @@ pub struct FeatureConfigPartial {
|
|||||||
pub merge_request: Option<MergeRequestFeatureConfigPartial>,
|
pub merge_request: Option<MergeRequestFeatureConfigPartial>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub orchestration: Option<FeatureFlagConfigPartial>,
|
pub orchestration: Option<FeatureFlagConfigPartial>,
|
||||||
#[serde(default)]
|
|
||||||
pub plugins: Option<FeatureFlagConfigPartial>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeatureConfigPartial {
|
impl FeatureConfigPartial {
|
||||||
@@ -145,7 +139,6 @@ impl FeatureConfigPartial {
|
|||||||
other.orchestration,
|
other.orchestration,
|
||||||
FeatureFlagConfigPartial::merge,
|
FeatureFlagConfigPartial::merge,
|
||||||
),
|
),
|
||||||
plugins: merge_option(self.plugins, other.plugins, FeatureFlagConfigPartial::merge),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,10 +363,6 @@ impl From<FeatureConfigPartial> for FeatureConfig {
|
|||||||
.orchestration
|
.orchestration
|
||||||
.map(FeatureFlagConfig::from)
|
.map(FeatureFlagConfig::from)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
plugins: value
|
|
||||||
.plugins
|
|
||||||
.map(FeatureFlagConfig::from)
|
|
||||||
.unwrap_or_default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,7 +506,6 @@ impl From<FeatureConfig> for FeatureConfigPartial {
|
|||||||
ticket: Some(value.ticket.into()),
|
ticket: Some(value.ticket.into()),
|
||||||
merge_request: Some(value.merge_request.into()),
|
merge_request: Some(value.merge_request.into()),
|
||||||
orchestration: Some(value.orchestration.into()),
|
orchestration: Some(value.orchestration.into()),
|
||||||
plugins: Some(value.plugins.into()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -654,6 +642,20 @@ pub(crate) fn reject_removed_manifest_fields(s: &str) -> Result<(), toml::de::Er
|
|||||||
"unknown field in manifest: memory (removed; configure feature.memory)",
|
"unknown field in manifest: memory (removed; configure feature.memory)",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if value.get("plugins").is_some() {
|
||||||
|
return Err(toml::de::Error::custom(
|
||||||
|
"unknown field in manifest: plugins (dynamic Plugins are not supported)",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if value
|
||||||
|
.get("feature")
|
||||||
|
.and_then(toml::Value::as_table)
|
||||||
|
.is_some_and(|table| table.contains_key("plugins"))
|
||||||
|
{
|
||||||
|
return Err(toml::de::Error::custom(
|
||||||
|
"unknown field in manifest: feature.plugins (dynamic Plugins are not supported)",
|
||||||
|
));
|
||||||
|
}
|
||||||
if value
|
if value
|
||||||
.get("feature")
|
.get("feature")
|
||||||
.and_then(toml::Value::as_table)
|
.and_then(toml::Value::as_table)
|
||||||
@@ -771,7 +773,6 @@ impl WorkerManifestConfig {
|
|||||||
PermissionConfigPartial::merge,
|
PermissionConfigPartial::merge,
|
||||||
),
|
),
|
||||||
feature: self.feature.merge(upper.feature),
|
feature: self.feature.merge(upper.feature),
|
||||||
plugins: merge_plugin_config(self.plugins, upper.plugins),
|
|
||||||
mcp: merge_mcp_config(self.mcp, upper.mcp),
|
mcp: merge_mcp_config(self.mcp, upper.mcp),
|
||||||
compaction: merge_option(
|
compaction: merge_option(
|
||||||
self.compaction,
|
self.compaction,
|
||||||
@@ -791,16 +792,6 @@ impl SkillsConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_plugin_config(mut base: PluginConfig, upper: PluginConfig) -> PluginConfig {
|
|
||||||
let upper_has_resolved_plan = upper.has_resolved_plan();
|
|
||||||
base.enabled.extend(upper.enabled);
|
|
||||||
if upper_has_resolved_plan {
|
|
||||||
base.resolved = upper.resolved;
|
|
||||||
base.diagnostics = upper.diagnostics;
|
|
||||||
}
|
|
||||||
base
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_mcp_config(mut base: McpConfig, upper: McpConfig) -> McpConfig {
|
fn merge_mcp_config(mut base: McpConfig, upper: McpConfig) -> McpConfig {
|
||||||
base.stdio_servers.extend(upper.stdio_servers);
|
base.stdio_servers.extend(upper.stdio_servers);
|
||||||
base
|
base
|
||||||
@@ -1289,7 +1280,6 @@ impl TryFrom<WorkerManifestConfig> for WorkerManifest {
|
|||||||
session,
|
session,
|
||||||
permissions,
|
permissions,
|
||||||
feature: FeatureConfig::from(cfg.feature),
|
feature: FeatureConfig::from(cfg.feature),
|
||||||
plugins: cfg.plugins,
|
|
||||||
mcp: cfg.mcp,
|
mcp: cfg.mcp,
|
||||||
compaction,
|
compaction,
|
||||||
web: cfg.web,
|
web: cfg.web,
|
||||||
@@ -1335,7 +1325,6 @@ mod tests {
|
|||||||
delegation_scope: ScopeConfig::default(),
|
delegation_scope: ScopeConfig::default(),
|
||||||
permissions: None,
|
permissions: None,
|
||||||
feature: FeatureConfigPartial::default(),
|
feature: FeatureConfigPartial::default(),
|
||||||
plugins: PluginConfig::default(),
|
|
||||||
mcp: McpConfig::default(),
|
mcp: McpConfig::default(),
|
||||||
session: None,
|
session: None,
|
||||||
compaction: None,
|
compaction: None,
|
||||||
|
|||||||
+52
-32
@@ -69,10 +69,6 @@ pub struct WorkerManifest {
|
|||||||
/// resolve disabled so Profile authors choose the exposed built-in surfaces.
|
/// resolve disabled so Profile authors choose the exposed built-in surfaces.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub feature: FeatureConfig,
|
pub feature: FeatureConfig,
|
||||||
/// Explicit plugin package enablement. Discovery remains read-only; only
|
|
||||||
/// source-qualified entries listed here may resolve to active plugin metadata.
|
|
||||||
#[serde(default)]
|
|
||||||
pub plugins: plugin::PluginConfig,
|
|
||||||
/// Explicit external Model Context Protocol provider configuration. This
|
/// Explicit external Model Context Protocol provider configuration. This
|
||||||
/// is config data only: declaring a server never starts a subprocess or
|
/// is config data only: declaring a server never starts a subprocess or
|
||||||
/// grants OS sandboxing. Runtime MCP lifecycle/registration is a separate
|
/// grants OS sandboxing. Runtime MCP lifecycle/registration is a separate
|
||||||
@@ -106,6 +102,7 @@ pub struct WorkerManifest {
|
|||||||
/// sessions, secrets, or resolved host state. Tool registration still applies
|
/// sessions, secrets, or resolved host state. Tool registration still applies
|
||||||
/// the normal scope, host-authority, backend, memory, and network checks.
|
/// the normal scope, host-authority, backend, memory, and network checks.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct FeatureConfig {
|
pub struct FeatureConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub task: FeatureFlagConfig,
|
pub task: FeatureFlagConfig,
|
||||||
@@ -135,8 +132,6 @@ pub struct FeatureConfig {
|
|||||||
pub merge_request: MergeRequestFeatureConfig,
|
pub merge_request: MergeRequestFeatureConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub orchestration: FeatureFlagConfig,
|
pub orchestration: FeatureFlagConfig,
|
||||||
#[serde(default)]
|
|
||||||
pub plugins: FeatureFlagConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FeatureConfig {
|
impl Default for FeatureConfig {
|
||||||
@@ -155,7 +150,6 @@ impl Default for FeatureConfig {
|
|||||||
ticket: TicketFeatureConfig::default(),
|
ticket: TicketFeatureConfig::default(),
|
||||||
merge_request: MergeRequestFeatureConfig::default(),
|
merge_request: MergeRequestFeatureConfig::default(),
|
||||||
orchestration: FeatureFlagConfig::disabled(),
|
orchestration: FeatureFlagConfig::disabled(),
|
||||||
plugins: FeatureFlagConfig::disabled(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -941,9 +935,7 @@ impl Default for CompactionConfig {
|
|||||||
|
|
||||||
impl WorkerManifest {
|
impl WorkerManifest {
|
||||||
pub fn requires_persisted_execution_snapshot(&self) -> bool {
|
pub fn requires_persisted_execution_snapshot(&self) -> bool {
|
||||||
self.profile.is_some()
|
self.profile.is_some() || self.feature.memory.workspace_settings.is_some()
|
||||||
|| self.plugins.has_resolved_plan()
|
|
||||||
|| self.feature.memory.workspace_settings.is_some()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a manifest from a TOML string.
|
/// Parse a manifest from a TOML string.
|
||||||
@@ -1322,33 +1314,61 @@ model_id = "claude-sonnet-4-20250514"
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_plugin_enablement_config() {
|
fn dynamic_plugin_manifest_config_is_rejected() {
|
||||||
let toml = format!(
|
let toml = format!(
|
||||||
"{MINIMAL_REQUIRED}\n\
|
"{MINIMAL_REQUIRED}\n\
|
||||||
[[plugins.enabled]]\n\
|
[[plugins.enabled]]\n\
|
||||||
id = \"project:example\"\n\
|
id = \"project:example\"\n"
|
||||||
version = \"0.1.0\"\n\
|
|
||||||
digest = \"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\
|
|
||||||
surfaces = [\"hook\"]\n\n\
|
|
||||||
[plugins.enabled.config]\n\
|
|
||||||
greeting = \"hello\"\n"
|
|
||||||
);
|
);
|
||||||
let manifest = WorkerManifest::from_toml(&toml).unwrap();
|
let error = WorkerManifest::from_toml(&toml).unwrap_err();
|
||||||
assert_eq!(manifest.plugins.enabled.len(), 1);
|
assert!(
|
||||||
let enabled = &manifest.plugins.enabled[0];
|
error
|
||||||
assert_eq!(enabled.id, "project:example");
|
.to_string()
|
||||||
assert_eq!(
|
.contains("dynamic Plugins are not supported"),
|
||||||
enabled.version.as_ref().map(|version| version.0.as_str()),
|
"unexpected error: {error}"
|
||||||
Some("0.1.0")
|
|
||||||
);
|
);
|
||||||
assert_eq!(enabled.surfaces, vec![plugin::PluginSurface::Hook]);
|
}
|
||||||
assert_eq!(
|
|
||||||
enabled
|
#[test]
|
||||||
.config
|
fn persisted_manifest_with_dynamic_plugin_plan_is_rejected() {
|
||||||
.as_ref()
|
let base =
|
||||||
.and_then(|value| value.get("greeting"))
|
serde_json::to_value(WorkerManifest::from_toml(MINIMAL_REQUIRED).unwrap()).unwrap();
|
||||||
.and_then(|value| value.as_str()),
|
|
||||||
Some("hello")
|
let mut top_level = base.clone();
|
||||||
|
top_level.as_object_mut().unwrap().insert(
|
||||||
|
"plugins".to_string(),
|
||||||
|
serde_json::json!({
|
||||||
|
"resolved": [{
|
||||||
|
"package_path": "/tmp/ambient.yoi-plugin"
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
let error = serde_json::from_value::<WorkerManifest>(top_level).unwrap_err();
|
||||||
|
assert!(error.to_string().contains("unknown field `plugins`"));
|
||||||
|
|
||||||
|
let mut nested = base;
|
||||||
|
nested
|
||||||
|
.get_mut("feature")
|
||||||
|
.unwrap()
|
||||||
|
.as_object_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert(
|
||||||
|
"plugins".to_string(),
|
||||||
|
serde_json::json!({ "enabled": true }),
|
||||||
|
);
|
||||||
|
let error = serde_json::from_value::<WorkerManifest>(nested).unwrap_err();
|
||||||
|
assert!(error.to_string().contains("unknown field `plugins`"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dynamic_plugin_feature_flag_is_rejected() {
|
||||||
|
let toml = format!("{MINIMAL_REQUIRED}\n[feature.plugins]\nenabled = true\n");
|
||||||
|
let error = WorkerManifest::from_toml(&toml).unwrap_err();
|
||||||
|
assert!(
|
||||||
|
error
|
||||||
|
.to_string()
|
||||||
|
.contains("dynamic Plugins are not supported"),
|
||||||
|
"unexpected error: {error}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+100
-1874
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@ use crate::config::{
|
|||||||
CompactionConfigPartial, FeatureConfigPartial, PermissionConfigPartial, SessionConfigPartial,
|
CompactionConfigPartial, FeatureConfigPartial, PermissionConfigPartial, SessionConfigPartial,
|
||||||
};
|
};
|
||||||
use crate::model::{AuthRef, ModelManifest};
|
use crate::model::{AuthRef, ModelManifest};
|
||||||
use crate::plugin::PluginConfig;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
EngineManifestConfig, McpConfig, McpStdioCwdPolicy, Permission, ResolveError, ScopeConfig,
|
EngineManifestConfig, McpConfig, McpStdioCwdPolicy, Permission, ResolveError, ScopeConfig,
|
||||||
ScopeRule, SkillsConfig, WebConfig, WorkerManifest, WorkerManifestConfig, WorkerMetaConfig,
|
ScopeRule, SkillsConfig, WebConfig, WorkerManifest, WorkerManifestConfig, WorkerMetaConfig,
|
||||||
@@ -148,7 +147,6 @@ pub enum WorkspaceAuthorityRequirement {
|
|||||||
MergeRequest,
|
MergeRequest,
|
||||||
Objective,
|
Objective,
|
||||||
Orchestration,
|
Orchestration,
|
||||||
Plugins,
|
|
||||||
Ticket,
|
Ticket,
|
||||||
Worker,
|
Worker,
|
||||||
}
|
}
|
||||||
@@ -162,7 +160,6 @@ impl fmt::Display for WorkspaceAuthorityRequirement {
|
|||||||
Self::MergeRequest => formatter.write_str("feature.merge_request"),
|
Self::MergeRequest => formatter.write_str("feature.merge_request"),
|
||||||
Self::Objective => formatter.write_str("feature.objective"),
|
Self::Objective => formatter.write_str("feature.objective"),
|
||||||
Self::Orchestration => formatter.write_str("feature.orchestration"),
|
Self::Orchestration => formatter.write_str("feature.orchestration"),
|
||||||
Self::Plugins => formatter.write_str("feature.plugins or plugin packages"),
|
|
||||||
Self::Ticket => formatter.write_str("feature.ticket"),
|
Self::Ticket => formatter.write_str("feature.ticket"),
|
||||||
Self::Worker => formatter.write_str("feature.worker"),
|
Self::Worker => formatter.write_str("feature.worker"),
|
||||||
}
|
}
|
||||||
@@ -202,9 +199,6 @@ pub fn validate_profile_execution_target(
|
|||||||
if feature.orchestration.enabled {
|
if feature.orchestration.enabled {
|
||||||
requirements.insert(WorkspaceAuthorityRequirement::Orchestration);
|
requirements.insert(WorkspaceAuthorityRequirement::Orchestration);
|
||||||
}
|
}
|
||||||
if feature.plugins.enabled || !manifest.plugins.is_empty() {
|
|
||||||
requirements.insert(WorkspaceAuthorityRequirement::Plugins);
|
|
||||||
}
|
|
||||||
if feature.ticket.enabled
|
if feature.ticket.enabled
|
||||||
|| feature.ticket.authoring
|
|| feature.ticket.authoring
|
||||||
|| feature.ticket.thread
|
|| feature.ticket.thread
|
||||||
@@ -638,7 +632,6 @@ fn resolve_profile_value(
|
|||||||
session: profile.session,
|
session: profile.session,
|
||||||
permissions: profile.permissions,
|
permissions: profile.permissions,
|
||||||
feature: profile.feature,
|
feature: profile.feature,
|
||||||
plugins: profile.plugins,
|
|
||||||
mcp: profile.mcp,
|
mcp: profile.mcp,
|
||||||
compaction,
|
compaction,
|
||||||
web: profile.web,
|
web: profile.web,
|
||||||
@@ -684,8 +677,6 @@ struct ProfileConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
feature: FeatureConfigPartial,
|
feature: FeatureConfigPartial,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
plugins: PluginConfig,
|
|
||||||
#[serde(default)]
|
|
||||||
mcp: McpConfig,
|
mcp: McpConfig,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
compaction: Option<serde_json::Value>,
|
compaction: Option<serde_json::Value>,
|
||||||
@@ -1267,6 +1258,51 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ambient_plugin_directories_do_not_affect_builtin_profile_resolution() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
let workspace = tmp.path().join("workspace/nested");
|
||||||
|
std::fs::create_dir_all(&workspace).unwrap();
|
||||||
|
for root in [tmp.path(), tmp.path().join("workspace").as_path()] {
|
||||||
|
let package = root.join(".yoi/plugins/broken.yoi-plugin");
|
||||||
|
std::fs::create_dir_all(package.parent().unwrap()).unwrap();
|
||||||
|
std::fs::write(package, b"malformed ambient package").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let resolved = ProfileResolver::new()
|
||||||
|
.with_workspace_base(&workspace)
|
||||||
|
.resolve_for_target(
|
||||||
|
&ProfileSelector::source_named(ProfileRegistrySource::Builtin, "default"),
|
||||||
|
ProfileResolveOptions::with_worker_name("standalone-worker"),
|
||||||
|
ProfileExecutionTarget::Standalone,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(resolved.manifest.worker.name, "standalone-worker");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn profile_rejects_dynamic_plugin_configuration() {
|
||||||
|
let tmp = TempDir::new().unwrap();
|
||||||
|
for body in [
|
||||||
|
"[feature.plugins]\nenabled = true\n",
|
||||||
|
"[[plugins.enabled]]\nid = \"explicit:example\"\n",
|
||||||
|
] {
|
||||||
|
let profile = write_profile(tmp.path(), "plugin.toml", body);
|
||||||
|
let error = ProfileResolver::new()
|
||||||
|
.with_workspace_base(tmp.path())
|
||||||
|
.resolve(
|
||||||
|
&ProfileSelector::path(profile),
|
||||||
|
ProfileResolveOptions::with_worker_name("runtime-worker"),
|
||||||
|
)
|
||||||
|
.unwrap_err();
|
||||||
|
assert!(
|
||||||
|
error.to_string().contains("unknown field"),
|
||||||
|
"unexpected error: {error}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builtin_default_resolves_as_a_standalone_local_capability_profile() {
|
fn builtin_default_resolves_as_a_standalone_local_capability_profile() {
|
||||||
let tmp = TempDir::new().unwrap();
|
let tmp = TempDir::new().unwrap();
|
||||||
@@ -1305,8 +1341,6 @@ mod tests {
|
|||||||
assert!(!resolved.manifest.feature.flow.enabled);
|
assert!(!resolved.manifest.feature.flow.enabled);
|
||||||
assert!(!resolved.manifest.feature.worker.enabled);
|
assert!(!resolved.manifest.feature.worker.enabled);
|
||||||
assert!(!resolved.manifest.feature.manage_workdir.enabled);
|
assert!(!resolved.manifest.feature.manage_workdir.enabled);
|
||||||
assert!(!resolved.manifest.feature.plugins.enabled);
|
|
||||||
assert!(resolved.manifest.plugins.is_empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ memory = { workspace = true }
|
|||||||
uuid = { workspace = true, features = ["v7"] }
|
uuid = { workspace = true, features = ["v7"] }
|
||||||
session-metrics = { workspace = true }
|
session-metrics = { workspace = true }
|
||||||
arc-swap = "1.9.1"
|
arc-swap = "1.9.1"
|
||||||
wasmtime = { version = "45.0.2", default-features = false, features = ["std", "runtime", "cranelift", "component-model"] }
|
|
||||||
tungstenite = { version = "0.28.0", default-features = false, features = ["handshake", "native-tls", "url"] }
|
tungstenite = { version = "0.28.0", default-features = false, features = ["handshake", "native-tls", "url"] }
|
||||||
tokio-tungstenite = { version = "0.28.0", default-features = false, features = ["native-tls", "connect"] }
|
tokio-tungstenite = { version = "0.28.0", default-features = false, features = ["native-tls", "connect"] }
|
||||||
futures-util = { version = "0.3", features = ["sink"] }
|
futures-util = { version = "0.3", features = ["sink"] }
|
||||||
@@ -53,5 +52,3 @@ dotenv = "0.15.0"
|
|||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
serial_test = "3.4.0"
|
serial_test = "3.4.0"
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
wat = "1.241.2"
|
|
||||||
yoi-plugin-pdk = { workspace = true }
|
|
||||||
|
|||||||
@@ -1385,12 +1385,6 @@ where
|
|||||||
feature_registry
|
feature_registry
|
||||||
.add_module(crate::feature::builtin::orchestration::orchestration_feature());
|
.add_module(crate::feature::builtin::orchestration::orchestration_feature());
|
||||||
}
|
}
|
||||||
for module in crate::feature::plugin::plugin_tool_features_if_enabled(
|
|
||||||
feature_config.plugins.enabled,
|
|
||||||
&worker.manifest().plugins,
|
|
||||||
) {
|
|
||||||
feature_registry = feature_registry.with_module(module);
|
|
||||||
}
|
|
||||||
if let Some(workspace_root) = local_workspace_root.as_ref() {
|
if let Some(workspace_root) = local_workspace_root.as_ref() {
|
||||||
if let Some(module) =
|
if let Some(module) =
|
||||||
crate::feature::mcp::discover_stdio_tool_feature(&mcp_config, workspace_root).await
|
crate::feature::mcp::discover_stdio_tool_feature(&mcp_config, workspace_root).await
|
||||||
|
|||||||
@@ -2211,7 +2211,6 @@ pub enum FeatureInstallError {
|
|||||||
pub mod background;
|
pub mod background;
|
||||||
pub mod builtin;
|
pub mod builtin;
|
||||||
pub mod mcp;
|
pub mod mcp;
|
||||||
pub mod plugin;
|
|
||||||
pub(crate) mod session;
|
pub(crate) mod session;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -2224,6 +2223,31 @@ mod tests {
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn worker_feature_composition_has_no_dynamic_plugin_install_path() {
|
||||||
|
let feature_source = include_str!("feature.rs")
|
||||||
|
.split("#[cfg(test)]")
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
let controller_source = include_str!("controller.rs")
|
||||||
|
.split("#[cfg(test)]")
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
for forbidden in [
|
||||||
|
"pub mod plugin",
|
||||||
|
"plugin_tool_features_if_enabled",
|
||||||
|
"ResolvedPluginRecord",
|
||||||
|
"read_resolved_plugin_runtime_component",
|
||||||
|
"feature.plugins",
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
!feature_source.contains(forbidden) && !controller_source.contains(forbidden),
|
||||||
|
"dynamic Plugin install path returned through {forbidden}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(FeatureId::builtin("task").as_str(), "builtin:task");
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DummyClient;
|
struct DummyClient;
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -127,7 +127,6 @@ where
|
|||||||
// parent manifest cannot accidentally grant its normal public tool surface
|
// parent manifest cannot accidentally grant its normal public tool surface
|
||||||
// or recursively schedule Feature-owned background work.
|
// or recursively schedule Feature-owned background work.
|
||||||
manifest.feature = Default::default();
|
manifest.feature = Default::default();
|
||||||
manifest.plugins = Default::default();
|
|
||||||
manifest.mcp = Default::default();
|
manifest.mcp = Default::default();
|
||||||
manifest.skills = None;
|
manifest.skills = None;
|
||||||
manifest.compaction = None;
|
manifest.compaction = None;
|
||||||
@@ -681,7 +680,6 @@ pub(crate) fn prepare_internal_worker_from_spec(
|
|||||||
} = spec;
|
} = spec;
|
||||||
manifest.worker.name = format!("internal-{}-{}", identity.kind, identity.run_id);
|
manifest.worker.name = format!("internal-{}-{}", identity.kind, identity.run_id);
|
||||||
manifest.feature = Default::default();
|
manifest.feature = Default::default();
|
||||||
manifest.plugins = Default::default();
|
|
||||||
manifest.mcp = Default::default();
|
manifest.mcp = Default::default();
|
||||||
manifest.skills = None;
|
manifest.skills = None;
|
||||||
manifest.compaction = None;
|
manifest.compaction = None;
|
||||||
|
|||||||
@@ -926,7 +926,6 @@ fn manifest_to_reusable_config(manifest: &WorkerManifest) -> WorkerManifestConfi
|
|||||||
rules: p.rules.clone(),
|
rules: p.rules.clone(),
|
||||||
}),
|
}),
|
||||||
feature: manifest.feature.clone().into(),
|
feature: manifest.feature.clone().into(),
|
||||||
plugins: manifest.plugins.clone(),
|
|
||||||
mcp: manifest.mcp.clone(),
|
mcp: manifest.mcp.clone(),
|
||||||
compaction: manifest
|
compaction: manifest
|
||||||
.compaction
|
.compaction
|
||||||
|
|||||||
@@ -7750,80 +7750,6 @@ permission = "read"
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn plugin_resolved_manifest_snapshot_is_persisted_without_profile() {
|
|
||||||
let mut manifest = WorkerManifest::from_toml(
|
|
||||||
r#"
|
|
||||||
[worker]
|
|
||||||
name = "plugin-snapshot"
|
|
||||||
|
|
||||||
[model]
|
|
||||||
scheme = "anthropic"
|
|
||||||
model_id = "claude-sonnet-4-20250514"
|
|
||||||
|
|
||||||
[engine]
|
|
||||||
instruction = "saved"
|
|
||||||
|
|
||||||
[[scope.allow]]
|
|
||||||
target = "/snapshot/workspace"
|
|
||||||
permission = "read"
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert!(manifest.profile.is_none());
|
|
||||||
assert!(
|
|
||||||
worker_metadata_for_manifest(&manifest, None, None, None)
|
|
||||||
.resolved_manifest_snapshot
|
|
||||||
.is_none()
|
|
||||||
);
|
|
||||||
|
|
||||||
manifest.plugins.resolved = vec![manifest::plugin::ResolvedPluginRecord {
|
|
||||||
identity: manifest::plugin::SourceQualifiedPluginId::new(
|
|
||||||
manifest::plugin::PluginSourceKind::Project,
|
|
||||||
"example",
|
|
||||||
),
|
|
||||||
source: manifest::plugin::PluginSourceKind::Project,
|
|
||||||
package_path: PathBuf::from("/snapshot/workspace/.yoi/plugins/example.yoi-plugin"),
|
|
||||||
package_label: "example.yoi-plugin".to_string(),
|
|
||||||
digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
||||||
.to_string(),
|
|
||||||
version: "0.1.0".to_string(),
|
|
||||||
manifest: manifest::plugin::PluginPackageManifest {
|
|
||||||
schema_version: 1,
|
|
||||||
id: "example".to_string(),
|
|
||||||
name: "Example".to_string(),
|
|
||||||
version: "0.1.0".to_string(),
|
|
||||||
description: None,
|
|
||||||
surfaces: vec![manifest::plugin::PluginSurface::Hook],
|
|
||||||
runtime: None,
|
|
||||||
hooks: vec![],
|
|
||||||
tools: vec![],
|
|
||||||
services: vec![],
|
|
||||||
ingresses: vec![],
|
|
||||||
permissions: vec![],
|
|
||||||
request: vec![],
|
|
||||||
websocket: vec![],
|
|
||||||
},
|
|
||||||
enabled_surfaces: vec![manifest::plugin::PluginSurface::Hook],
|
|
||||||
grants: manifest::plugin::PluginGrantConfig::default(),
|
|
||||||
config: None,
|
|
||||||
}];
|
|
||||||
|
|
||||||
let metadata = worker_metadata_for_manifest(&manifest, None, None, None);
|
|
||||||
let snapshot = metadata
|
|
||||||
.resolved_manifest_snapshot
|
|
||||||
.expect("plugin-resolved manifest should be snapshotted");
|
|
||||||
let restored = manifest::read_persisted_worker_manifest_snapshot(snapshot).unwrap();
|
|
||||||
|
|
||||||
assert!(restored.profile.is_none());
|
|
||||||
assert_eq!(restored.plugins.resolved.len(), 1);
|
|
||||||
assert_eq!(
|
|
||||||
restored.plugins.resolved[0].digest,
|
|
||||||
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
||||||
);
|
|
||||||
assert_eq!(restored.plugins.resolved[0].version, "0.1.0");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
+26
-75
@@ -1420,7 +1420,7 @@ async fn run_login(backend_url: &str, no_wait: bool) -> Result<(), ParseError> {
|
|||||||
fn parse_plugin_args(args: &[String]) -> Result<plugin_cli::PluginCliCommand, ParseError> {
|
fn parse_plugin_args(args: &[String]) -> Result<plugin_cli::PluginCliCommand, ParseError> {
|
||||||
let Some((subcommand, rest)) = args.split_first() else {
|
let Some((subcommand, rest)) = args.split_first() else {
|
||||||
return Err(ParseError(
|
return Err(ParseError(
|
||||||
"yoi plugin requires `new`, `check`, `pack`, `list`, or `show <ref>`".to_string(),
|
"yoi plugin requires `new`, `check`, or `pack`".to_string(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
match subcommand.as_str() {
|
match subcommand.as_str() {
|
||||||
@@ -1472,30 +1472,6 @@ fn parse_plugin_args(args: &[String]) -> Result<plugin_cli::PluginCliCommand, Pa
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"list" => {
|
|
||||||
let (plugin_args, positional) = parse_plugin_common_args(rest)?;
|
|
||||||
if !positional.is_empty() {
|
|
||||||
return Err(ParseError(
|
|
||||||
"yoi plugin list does not accept positional arguments".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(plugin_cli::PluginCliCommand::List(plugin_args))
|
|
||||||
}
|
|
||||||
"show" => {
|
|
||||||
let (plugin_args, positional) = parse_plugin_common_args(rest)?;
|
|
||||||
match positional.as_slice() {
|
|
||||||
[reference] => Ok(plugin_cli::PluginCliCommand::Show {
|
|
||||||
reference: reference.clone(),
|
|
||||||
args: plugin_args,
|
|
||||||
}),
|
|
||||||
[] => Err(ParseError(
|
|
||||||
"yoi plugin show requires a plugin ref".to_string(),
|
|
||||||
)),
|
|
||||||
_ => Err(ParseError(
|
|
||||||
"yoi plugin show accepts exactly one plugin ref".to_string(),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"--help" | "-h" => Err(ParseError(plugin_usage().to_string())),
|
"--help" | "-h" => Err(ParseError(plugin_usage().to_string())),
|
||||||
other => Err(ParseError(format!(
|
other => Err(ParseError(format!(
|
||||||
"unknown yoi plugin subcommand `{other}`"
|
"unknown yoi plugin subcommand `{other}`"
|
||||||
@@ -1513,35 +1489,7 @@ fn parse_plugin_common_args(
|
|||||||
let arg = &args[index];
|
let arg = &args[index];
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
"--json" => parsed.json = true,
|
"--json" => parsed.json = true,
|
||||||
"--workspace" => {
|
|
||||||
index += 1;
|
|
||||||
let Some(value) = args.get(index) else {
|
|
||||||
return Err(ParseError("--workspace requires a value".to_string()));
|
|
||||||
};
|
|
||||||
parsed.workspace = Some(PathBuf::from(value));
|
|
||||||
}
|
|
||||||
"--profile" => {
|
|
||||||
index += 1;
|
|
||||||
let Some(value) = args.get(index) else {
|
|
||||||
return Err(ParseError("--profile requires a value".to_string()));
|
|
||||||
};
|
|
||||||
parsed.profile = Some(value.clone());
|
|
||||||
}
|
|
||||||
"--help" | "-h" => return Err(ParseError(plugin_usage().to_string())),
|
"--help" | "-h" => return Err(ParseError(plugin_usage().to_string())),
|
||||||
_ if arg.starts_with("--workspace=") => {
|
|
||||||
let value = arg.trim_start_matches("--workspace=");
|
|
||||||
if value.is_empty() {
|
|
||||||
return Err(ParseError("--workspace requires a value".to_string()));
|
|
||||||
}
|
|
||||||
parsed.workspace = Some(PathBuf::from(value));
|
|
||||||
}
|
|
||||||
_ if arg.starts_with("--profile=") => {
|
|
||||||
let value = arg.trim_start_matches("--profile=");
|
|
||||||
if value.is_empty() {
|
|
||||||
return Err(ParseError("--profile requires a value".to_string()));
|
|
||||||
}
|
|
||||||
parsed.profile = Some(value.to_string());
|
|
||||||
}
|
|
||||||
_ if arg.starts_with('-') => {
|
_ if arg.starts_with('-') => {
|
||||||
return Err(ParseError(format!("unknown yoi plugin option `{arg}`")));
|
return Err(ParseError(format!("unknown yoi plugin option `{arg}`")));
|
||||||
}
|
}
|
||||||
@@ -1581,7 +1529,7 @@ fn parse_plugin_pack_args(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn plugin_usage() -> &'static str {
|
fn plugin_usage() -> &'static str {
|
||||||
"usage: yoi plugin new <rust-component-tool|rust-component-service> <path-or-name> [--json]\n yoi plugin check <path-or-package> [--json]\n yoi plugin pack <path> [--output <file>] [--json]\n yoi plugin list [--workspace PATH] [--profile REF] [--json]\n yoi plugin show <ref> [--workspace PATH] [--profile REF] [--json]"
|
"usage: yoi plugin new <rust-component-tool|rust-component-service> <path-or-name> [--json]\n yoi plugin check <path-or-package> [--json]\n yoi plugin pack <path> [--output <file>] [--json]"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_mcp_args(args: &[String]) -> Result<mcp_cli::McpCliCommand, ParseError> {
|
fn parse_mcp_args(args: &[String]) -> Result<mcp_cli::McpCliCommand, ParseError> {
|
||||||
@@ -1808,7 +1756,7 @@ Host commands:
|
|||||||
worker [WORKER_OPTIONS] Run the direct Worker process entrypoint
|
worker [WORKER_OPTIONS] Run the direct Worker process entrypoint
|
||||||
ticket <COMMAND> Manage Tickets through a Backend target
|
ticket <COMMAND> Manage Tickets through a Backend target
|
||||||
objective <COMMAND> Manage Objectives through a Backend target
|
objective <COMMAND> Manage Objectives through a Backend target
|
||||||
plugin <COMMAND> Build/check/list/show plugins
|
plugin <COMMAND> Author/check/pack explicit Plugin packages
|
||||||
mcp <COMMAND> Inspect configured MCP servers
|
mcp <COMMAND> Inspect configured MCP servers
|
||||||
memory lint Lint local memory files
|
memory lint Lint local memory files
|
||||||
session <COMMAND> Inspect/prune Standalone session logs
|
session <COMMAND> Inspect/prune Standalone session logs
|
||||||
@@ -2065,6 +2013,12 @@ backend = "shared"
|
|||||||
assert!(TOP_LEVEL_HELP.contains("default_connection = \"local\""));
|
assert!(TOP_LEVEL_HELP.contains("default_connection = \"local\""));
|
||||||
assert!(TOP_LEVEL_HELP.contains("yoi-server"));
|
assert!(TOP_LEVEL_HELP.contains("yoi-server"));
|
||||||
assert!(TOP_LEVEL_HELP.contains("yoi-runtime"));
|
assert!(TOP_LEVEL_HELP.contains("yoi-runtime"));
|
||||||
|
assert!(
|
||||||
|
TOP_LEVEL_HELP.contains(
|
||||||
|
"plugin <COMMAND> Author/check/pack explicit Plugin packages"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert!(!TOP_LEVEL_HELP.contains("Build/check/list/show plugins"));
|
||||||
assert!(!TOP_LEVEL_HELP.contains("yoi workspace"));
|
assert!(!TOP_LEVEL_HELP.contains("yoi workspace"));
|
||||||
assert!(!TOP_LEVEL_HELP.contains("yoi server"));
|
assert!(!TOP_LEVEL_HELP.contains("yoi server"));
|
||||||
assert!(!TOP_LEVEL_HELP.contains("TARGET_OPTIONS"));
|
assert!(!TOP_LEVEL_HELP.contains("TARGET_OPTIONS"));
|
||||||
@@ -2843,29 +2797,26 @@ backend = "shared"
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_plugin_list_and_show() {
|
fn plugin_cli_rejects_ambient_catalog_commands_and_options() {
|
||||||
match parse_args_from(["plugin", "list", "--workspace=/tmp/ws", "--json"]).unwrap() {
|
for args in [
|
||||||
Mode::Plugin(plugin_cli::PluginCliCommand::List(options)) => {
|
vec!["plugin", "list"],
|
||||||
assert_eq!(options.workspace, Some(PathBuf::from("/tmp/ws")));
|
vec!["plugin", "show", "project:echo"],
|
||||||
assert!(options.json);
|
vec!["plugin", "check", "plugin", "--workspace=/tmp/ws"],
|
||||||
}
|
vec!["plugin", "check", "plugin", "--profile", "project:inspect"],
|
||||||
_ => panic!("expected Plugin list mode"),
|
] {
|
||||||
|
let error = parse_args_from(args).unwrap_err();
|
||||||
|
assert!(
|
||||||
|
error.0.contains("unknown yoi plugin"),
|
||||||
|
"unexpected error: {error}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match parse_args_from([
|
match parse_args_from(["plugin", "check", "plugin.yoi-plugin", "--json"]).unwrap() {
|
||||||
"plugin",
|
Mode::Plugin(plugin_cli::PluginCliCommand::Check { input, args }) => {
|
||||||
"show",
|
assert_eq!(input, PathBuf::from("plugin.yoi-plugin"));
|
||||||
"project:echo",
|
assert!(args.json);
|
||||||
"--profile",
|
|
||||||
"project:inspect",
|
|
||||||
])
|
|
||||||
.unwrap()
|
|
||||||
{
|
|
||||||
Mode::Plugin(plugin_cli::PluginCliCommand::Show { reference, args }) => {
|
|
||||||
assert_eq!(reference, "project:echo");
|
|
||||||
assert_eq!(args.profile.as_deref(), Some("project:inspect"));
|
|
||||||
}
|
}
|
||||||
_ => panic!("expected Plugin show mode"),
|
_ => panic!("expected Plugin check mode"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+71
-2267
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -14,8 +14,8 @@ It is not a dumping ground for external research, old plans, API inventories, or
|
|||||||
6. [`design/flow-state-graph.md`](design/flow-state-graph.md) — Workspace Flow sources, immutable revisions, transition attempts, and bounded internal verification.
|
6. [`design/flow-state-graph.md`](design/flow-state-graph.md) — Workspace Flow sources, immutable revisions, transition attempts, and bounded internal verification.
|
||||||
7. [`design/profiles-manifests-prompts.md`](design/profiles-manifests-prompts.md) — reusable Profiles, resolved Manifests, and prompt resources.
|
7. [`design/profiles-manifests-prompts.md`](design/profiles-manifests-prompts.md) — reusable Profiles, resolved Manifests, and prompt resources.
|
||||||
8. [`design/tool-permissions-scope.md`](design/tool-permissions-scope.md) — tool policy and filesystem scope.
|
8. [`design/tool-permissions-scope.md`](design/tool-permissions-scope.md) — tool policy and filesystem scope.
|
||||||
9. [`design/plugin-packages.md`](design/plugin-packages.md) — plugin package distribution, discovery, and enablement boundaries.
|
9. [`design/plugin-packages.md`](design/plugin-packages.md) — the retained offline package format and the prohibition on ambient Plugin authority.
|
||||||
10. [`development/plugin-development.md`](development/plugin-development.md) — how to build, package, enable, and inspect Yoi Plugins.
|
10. [`development/plugin-development.md`](development/plugin-development.md) — explicit-path offline Plugin package authoring and inspection.
|
||||||
11. [`design/memory-knowledge.md`](design/memory-knowledge.md) — generated memory and audit records.
|
11. [`design/memory-knowledge.md`](design/memory-knowledge.md) — generated memory and audit records.
|
||||||
12. [`design/workspace-kanban-orchestrator-runtime.md`](design/workspace-kanban-orchestrator-runtime.md) — how Kanban operations become durable orchestration events and backend-internal routing decisions.
|
12. [`design/workspace-kanban-orchestrator-runtime.md`](design/workspace-kanban-orchestrator-runtime.md) — how Kanban operations become durable orchestration events and backend-internal routing decisions.
|
||||||
13. [`design/workspace-runtime-docker.md`](design/workspace-runtime-docker.md) — the WebUI / Backend / Runtime split, Docker image layout, worker launch path, and workdir materialization boundary.
|
13. [`design/workspace-runtime-docker.md`](design/workspace-runtime-docker.md) — the WebUI / Backend / Runtime split, Docker image layout, worker launch path, and workdir materialization boundary.
|
||||||
|
|||||||
@@ -1,177 +1,29 @@
|
|||||||
# Plugin Component Model migration
|
# Plugin Component Model boundary
|
||||||
|
|
||||||
Yoi's original Plugin Tool runtime used a narrow core-WebAssembly ABI. That was the right MVP shape because it made sandboxing, bounded input/output, and fail-closed host imports explicit, but it is no longer the public authoring interface.
|
Dynamic Worker Plugin execution is not part of the current product. The `.yoi-plugin` component metadata retained in `manifest` is an offline package-format contract only.
|
||||||
|
|
||||||
The supported runtime kind is now `wasm-component`, using the WebAssembly Component Model for Plugin Tool authoring and host APIs. Component Model adoption means Plugin interfaces are described as typed WIT worlds and lowered through the canonical ABI, instead of every Plugin author or SDK wrapper hand-writing pointer/length memory plumbing.
|
## Current behavior
|
||||||
|
|
||||||
## What Component Model changes
|
- Worker creation and restore install no dynamic Plugin modules.
|
||||||
|
- Manifest/Profile input rejects `plugins` and `feature.plugins`.
|
||||||
|
- Runtime and Server startup perform no repository, ancestor, cwd, or user-data Plugin discovery.
|
||||||
|
- No persisted local `package_path` is execution authority.
|
||||||
|
- Only statically compiled trusted built-in Features contribute Worker capabilities.
|
||||||
|
- `yoi plugin check` parses an explicitly named directory or package without instantiating a component.
|
||||||
|
|
||||||
A core Wasm module exposes low-level functions and memory. Yoi's current Plugin Tool ABI is shaped like this:
|
The package validator may reject legacy core-Wasm artifacts and require Component Model metadata, but passing validation does not make an artifact installable or executable.
|
||||||
|
|
||||||
```text
|
## Future platform constraints
|
||||||
export memory
|
|
||||||
export yoi_tool_call() -> i32
|
|
||||||
import yoi:tool/tool_name_len() -> i32
|
|
||||||
import yoi:tool/tool_name_read(ptr, len) -> i32
|
|
||||||
import yoi:tool/input_len() -> i32
|
|
||||||
import yoi:tool/input_read(ptr, len) -> i32
|
|
||||||
import yoi:tool/output_write(ptr, len) -> i32
|
|
||||||
```
|
|
||||||
|
|
||||||
This is small and auditable, but it makes raw ABI details part of the authoring model. A Component Model world can instead describe a typed contract:
|
A future Server Plugin platform may execute Wasmtime Component Model packages only after the architecture is implemented as a coherent authority boundary:
|
||||||
|
|
||||||
```wit
|
1. an operator installs an immutable package into Server-owned artifact authority;
|
||||||
package yoi:plugin;
|
2. a Workspace owner selects an installed package through an immutable Addon revision;
|
||||||
|
3. Backend authors a per-Worker execution plan containing exact identities, digests, configuration, and bounded grants;
|
||||||
|
4. Runtime fetches only Server-authorized digests and verifies package bytes and execution-plan identity;
|
||||||
|
5. Runtime instantiates a fresh bounded Wasmtime Store with no ambient WASI authority; and
|
||||||
|
6. restore uses the persisted execution plan and exact artifact rather than current Workspace settings or a filesystem path.
|
||||||
|
|
||||||
interface tool {
|
Default components receive no filesystem, sockets, environment, clocks, randomness, subprocess, Workdir, broad Workspace client, credential, or network authority. Any host import must be narrow, typed, explicitly granted, live-revalidated where necessary, bounded, and audited.
|
||||||
record request {
|
|
||||||
tool-name: string,
|
|
||||||
input-json: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
record response {
|
This future platform must not restore `.yoi/plugins`, user-data catalogs, cwd/ancestor discovery, native dynamic libraries, downloaded Cargo manifests, or local paths as compatibility authority.
|
||||||
output-json: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
variant tool-error {
|
|
||||||
invalid-input(string),
|
|
||||||
denied(string),
|
|
||||||
failed(string),
|
|
||||||
}
|
|
||||||
|
|
||||||
run: func(req: request) -> result<response, tool-error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
world tool-plugin {
|
|
||||||
export tool;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The exact WIT is still design work, but the important boundary is fixed: the Plugin author sees typed values and generated bindings; the host sees typed imports/exports; Yoi still enforces package enablement and Plugin grants outside the component.
|
|
||||||
|
|
||||||
## External patterns considered
|
|
||||||
|
|
||||||
Common Wasm extension systems normally ship more than a runtime:
|
|
||||||
|
|
||||||
- Extism-style systems provide host runtimes plus language PDKs. Plugin authors write normal typed functions while the PDK hides the raw ABI and host functions remain explicit.
|
|
||||||
- Spin-style systems combine a manifest, language SDK/templates, default-deny outbound/file capabilities, and Wasm components.
|
|
||||||
- wasmCloud-style systems separate components from capability providers and connect them through typed interfaces.
|
|
||||||
- The Component Model standardizes the interface layer with WIT and canonical ABI so host APIs can be versioned and bindings generated across languages.
|
|
||||||
|
|
||||||
The shared lesson is that a usable Wasm Plugin system needs a manifest, explicit capabilities, generated or hand-written SDK bindings, examples/templates, inspection tooling, and a versioned ABI. Yoi already has the manifest/discovery/enablement/grant/runtime foundation; the missing long-term piece is the typed component authoring interface.
|
|
||||||
|
|
||||||
## Yoi policy
|
|
||||||
|
|
||||||
Adopting the Component Model must not change Yoi's authority model:
|
|
||||||
|
|
||||||
- Package discovery is inventory only and does not register or execute a Plugin.
|
|
||||||
- Explicit enablement is required before any Tool surface is registered.
|
|
||||||
- Plugin grants are required before runtime execution and before `https` / `fs` / future host API calls.
|
|
||||||
- Component imports are not authority by themselves; host-side grant checks remain authoritative.
|
|
||||||
- Tool calls and Tool results continue through the ordinary ToolRegistry and Engine history path.
|
|
||||||
- No hidden context injection is introduced by component imports, resources, prompts, or SDK helpers.
|
|
||||||
- Plugin SDKs and templates are authoring aids, not trust boundaries.
|
|
||||||
|
|
||||||
## Migration shape
|
|
||||||
|
|
||||||
`runtime.kind = "wasm-component"` is the sole public Plugin runtime kind. Legacy raw core-Wasm declarations (`kind = "wasm"` / `abi = "yoi-plugin-wasm-1"`) are rejected by manifest validation and are surfaced only as bounded diagnostics; they are not active/eligible Plugins and are not executed.
|
|
||||||
|
|
||||||
The migration is now focused on the component surface:
|
|
||||||
|
|
||||||
1. Keep WIT packages/worlds for Tool Plugin and initial host APIs versioned under `resources/plugin/wit`.
|
|
||||||
2. Keep manifest/schema support centered on `runtime.kind = "wasm-component"`.
|
|
||||||
3. Keep the component runtime backend and typed host import/export binding as the active execution path.
|
|
||||||
4. Port future host API designs to WIT-compatible interfaces.
|
|
||||||
5. Keep the Rust PDK/template aligned with the component world.
|
|
||||||
|
|
||||||
## Runtime/backend caution
|
|
||||||
|
|
||||||
The legacy core-Wasm implementation used `wasmi` as a transitional backend. The active Plugin Tool runtime is now selected by package runtime metadata and executed through `wasmtime::component`; discovery and static inspection must continue to avoid executing package code.
|
|
||||||
|
|
||||||
Keep the component backend selected by package runtime metadata and Profile/feature policy. Do not make all Plugin packages depend on component execution during discovery or inspection.
|
|
||||||
|
|
||||||
## Relationship to pending host APIs
|
|
||||||
|
|
||||||
`https` and `fs` host API Tickets should avoid baking in raw pointer/length interfaces as the long-term authoring contract. If they land before the component runtime, implement them in a way that can be represented as WIT records/results later, and document raw ABI wrappers as transitional.
|
|
||||||
|
|
||||||
For example, `https` should be modeled as typed request/response data with explicit grant checks for host/method/path/body bounds. `fs` should be modeled as scoped read/list/write operations with path normalization and root-escape rejection. Those concepts translate well to WIT.
|
|
||||||
|
|
||||||
## Non-goals
|
|
||||||
|
|
||||||
- Component Model adoption does not imply WASI filesystem/network access.
|
|
||||||
- It does not replace Plugin grants with WIT imports.
|
|
||||||
- It does not introduce Service, Ingress, WebSocket, or inbound HTTP by itself.
|
|
||||||
- It does not merge Plugin and MCP. MCP remains a separate untrusted tool/resource/prompt bridge with its own policy.
|
|
||||||
|
|
||||||
## Implemented runtime boundary
|
|
||||||
|
|
||||||
Plugin Tool packages now select the runtime explicitly in `plugin.toml`:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[runtime]
|
|
||||||
kind = "wasm-component"
|
|
||||||
component = "plugin.component.wasm"
|
|
||||||
world = "yoi:plugin/tool@1.0.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
Legacy core-Wasm metadata is accepted only far enough to produce migration diagnostics: package checks and discovery reject `kind = "wasm"` / `abi = "yoi-plugin-wasm-1"`, `list`/`show` report those packages as rejected rather than active/eligible, and the active runtime path does not execute them.
|
|
||||||
|
|
||||||
The component runtime uses `wasmtime::component` and expects the exported world
|
|
||||||
`yoi:plugin/tool@1.0.0` with a `call(tool-name: string, input-json: string) ->
|
|
||||||
string` export. The returned string is the normal ToolOutput JSON, so
|
|
||||||
registration and execution still flow through the existing ToolRegistry and
|
|
||||||
Engine Tool-result history path.
|
|
||||||
|
|
||||||
Host imports are stable names under `yoi:host/*@1.0.0`; the repository WIT files
|
|
||||||
live in `resources/plugin/wit/`. Importing `yoi:host/request@1.0.0` or
|
|
||||||
`yoi:host/fs@1.0.0` is not authority. The runtime checks package grants before
|
|
||||||
component instantiation and checks again on every host call. No WASI filesystem,
|
|
||||||
network, environment, or other ambient imports are linked.
|
|
||||||
|
|
||||||
Static discovery and `yoi plugin list/show` only parse package manifests and
|
|
||||||
reported runtime metadata. They do not instantiate or execute the component.
|
|
||||||
Wrong `world`, missing artifact metadata, missing `call` export, unsupported
|
|
||||||
imports, or core-Wasm bytes in a component package all fail closed with bounded
|
|
||||||
Plugin diagnostics or ordinary Tool errors.
|
|
||||||
|
|
||||||
See `docs/examples/plugin-component-tool/lib.rs` and the embedded
|
|
||||||
`resources/plugin/templates/rust-component-tool/` starter for the preferred
|
|
||||||
Rust PDK authoring path. `yoi-plugin-pdk` is guest-side only: it re-exports
|
|
||||||
`wit-bindgen`, provides typed JSON input/output helpers, renders bounded
|
|
||||||
`ToolError` values as ordinary ToolOutput JSON, and does not depend on host
|
|
||||||
runtime crates or grant authority. Package authors should generate bindings from
|
|
||||||
`resources/plugin/wit`, build a component artifact, and set the component
|
|
||||||
runtime metadata above.
|
|
||||||
|
|
||||||
### v1 request/response shape
|
|
||||||
|
|
||||||
The v1 component world intentionally keeps Tool input, Tool output, and host API
|
|
||||||
payloads as JSON strings. This is a migration bridge that preserves the existing
|
|
||||||
ToolOutput schema, Tool history behavior, grant checks, and raw-Wasm host API
|
|
||||||
semantics while moving package authors onto WIT/canonical ABI bindings.
|
|
||||||
Structured WIT records for Tool requests/responses/errors and host request/FS
|
|
||||||
payloads are deferred to a follow-up API-design step rather than accidentally
|
|
||||||
omitted.
|
|
||||||
|
|
||||||
## Instance lifecycle surface
|
|
||||||
|
|
||||||
The first instance-capable world is `yoi:plugin/instance@1.0.0`. It moves
|
|
||||||
runtime ownership from per-Tool artifact execution to a host-managed
|
|
||||||
`PluginInstance`. The same instance handles Tool, Service, and Ingress surfaces,
|
|
||||||
so Plugin state/config/diagnostics can be shared without bypassing Yoi's normal
|
|
||||||
authority model.
|
|
||||||
|
|
||||||
Important boundaries:
|
|
||||||
|
|
||||||
- Tool calls still enter through `ToolRegistry` and return ordinary `ToolOutput`
|
|
||||||
that is visible in the Engine history path.
|
|
||||||
- Service and Ingress grants are separate from Tool grants. Sharing an instance
|
|
||||||
does not authorize a surface that lacks its own `surface.*` and per-surface
|
|
||||||
permission/grant.
|
|
||||||
- Ingress delivery accepts bounded typed untrusted events and returns explicit
|
|
||||||
JSON to the host. It does not call model Tools or mutate LLM context/history.
|
|
||||||
- Legacy raw-wasm and `yoi:plugin/tool@1.0.0` component packages are adapted
|
|
||||||
behind `PluginInstanceRegistry` for compatibility rather than executed through
|
|
||||||
a separate authority path.
|
|
||||||
- Host APIs such as `https` and `fs` remain independently grant-gated and still
|
|
||||||
reject ambient filesystem/network authority.
|
|
||||||
|
|||||||
+20
-208
@@ -1,219 +1,31 @@
|
|||||||
# Plugin packages and discovery
|
# Plugin packages
|
||||||
|
|
||||||
Plugin packages are a distribution format, not an authority boundary. A package can be found on disk, inspected, validated, and cached without registering any Hook, exposing any Tool, starting any process, or initializing any WASM module.
|
Yoi retains `.yoi-plugin` as an offline authoring and inspection format. The format is not a Worker capability source.
|
||||||
|
|
||||||
The initial goal is a durable `.yoi-plugin` package format that later Tickets can implement in independent layers: discovery, archive validation/cache materialization, manifest/profile enablement, Plugin permission policy, declarative hooks, WASM runtime support, and any future MCP bridge.
|
## Current product boundary
|
||||||
|
|
||||||
## Package shape
|
Normal Worker creation, restore, Profile/Manifest resolution, Server/Runtime startup, and CLI execution do not discover Plugin catalogs from:
|
||||||
|
|
||||||
A `.yoi-plugin` file is a single-file archive. The archive format is a constrained ZIP profile because it is easy to inspect without executing code and can carry text manifests, WebAssembly Component Model modules, schemas, and license material.
|
- repository or ancestor `.yoi/plugins` directories;
|
||||||
|
- user-data Plugin directories;
|
||||||
|
- the current working directory; or
|
||||||
|
- persisted local `package_path` values.
|
||||||
|
|
||||||
The archive root must contain `plugin.toml` directly at the root. Packages should not require a wrapping directory whose name must match the plugin id.
|
`plugins` and `feature.plugins` are rejected in Worker Manifest/Profile input. Dynamic Plugin Tools, Services, Ingress handlers, and WASM components are not installed or executed. Worker capabilities come only from statically compiled trusted built-in Features.
|
||||||
|
|
||||||
Recommended root layout:
|
## Offline format operations
|
||||||
|
|
||||||
```text
|
The CLI keeps only operations whose input or destination is explicit:
|
||||||
plugin.toml # required package manifest
|
|
||||||
plugin.component.wasm # required when plugin.toml declares the component runtime
|
```sh
|
||||||
hooks/*.toml # optional declarative hook definitions
|
yoi plugin new rust-component-tool ./example-plugin
|
||||||
schemas/*.schema.json # optional JSON schemas for configuration or tool input/output
|
yoi plugin check ./example-plugin
|
||||||
README.md # recommended human description
|
yoi plugin pack ./example-plugin --output ./example-plugin.yoi-plugin
|
||||||
LICENSE* # recommended license text
|
yoi plugin check ./example-plugin.yoi-plugin
|
||||||
assets/** # optional non-executable data assets
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The package layout is intentionally data-first. Placing a package in a store must never execute `module.wasm`, register hook metadata, or scan assets as prompts. Those steps happen only after explicit enablement and policy resolution.
|
These commands parse, validate, or write the named local artifact. They do not scan a Workspace, mutate Profile/Manifest configuration, install a package, register Worker Tools, or execute Plugin code. `plugin list` and `plugin show` were removed because their catalog semantics depended on ambient repository and user-data stores.
|
||||||
|
|
||||||
## `plugin.toml`
|
## Future installation authority
|
||||||
|
|
||||||
`plugin.toml` is the package authority for package identity and declared needs. It is not the authority for runtime grants.
|
Server-installed Plugin packages and Addons are future work. That platform must provide explicit immutable package identity and digest, Server-owned artifact delivery and Workspace selection, a Backend-authored per-Worker execution plan, Runtime verification, and sandboxed execution. It must not restore repository-local or user-data discovery as a compatibility fallback.
|
||||||
|
|
||||||
Currently implemented strict `plugin.toml` shape:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
schema_version = 1
|
|
||||||
id = "example.summarizer"
|
|
||||||
name = "Example Summarizer"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Adds a custom summary command."
|
|
||||||
surfaces = ["hook"]
|
|
||||||
|
|
||||||
[[hooks]]
|
|
||||||
id = "summary"
|
|
||||||
file = "hooks/summary.md"
|
|
||||||
```
|
|
||||||
|
|
||||||
The package archive must contain both root `plugin.toml` and referenced runtime/content entries. Component runtime metadata is explicit and static inspection never executes the artifact:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[runtime]
|
|
||||||
kind = "wasm-component"
|
|
||||||
component = "plugin.component.wasm"
|
|
||||||
world = "yoi:plugin/tool@1.0.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
`wasm-component` is the public/recommended runtime kind, recorded in [Plugin Component Model migration](plugin-component-model.md). Legacy raw core-Wasm declarations (`kind = "wasm"` / `abi = "yoi-plugin-wasm-1"`) are retired: manifest validation rejects them and CLI inspection reports the package as rejected rather than active/eligible.
|
|
||||||
|
|
||||||
First-pass fields accepted by the parser:
|
|
||||||
|
|
||||||
- `schema_version`: required integer; unsupported versions fail closed.
|
|
||||||
- `id`: required unqualified local id. It is scoped by the source that discovered the package; it is not globally unique by itself.
|
|
||||||
- `name`, `version`, `description`: human metadata used in listings and diagnostics.
|
|
||||||
- `surfaces`: optional declared contribution surface names.
|
|
||||||
- `runtime`: optional component runtime metadata. Discovery records metadata and never executes it; unsupported/retired runtime kinds fail closed.
|
|
||||||
- `hooks`: optional hook metadata. Discovery records metadata and does not register hooks.
|
|
||||||
|
|
||||||
Future descriptor sections such as `[package]`, `[permissions]`, richer `contributions`, or `runtime.kind = "declarative"` are aspirational and are intentionally rejected by the current strict parser until implemented safely.
|
|
||||||
|
|
||||||
The `source` is not read from `plugin.toml`. It is assigned by the store that discovered the package.
|
|
||||||
|
|
||||||
## Stores, sources, and trust
|
|
||||||
|
|
||||||
Discovery should scan explicit stores and attach a source kind to each package:
|
|
||||||
|
|
||||||
- `builtin:<id>`: packages shipped with Yoi or installed as part of the binary distribution.
|
|
||||||
- `user:<id>`: packages discovered under `${XDG_DATA_HOME:-~/.local/share}/yoi/plugins/`.
|
|
||||||
- `project:<id>`: packages discovered under `<workspace>/.yoi/plugins/`.
|
|
||||||
|
|
||||||
Packages under `${XDG_DATA_HOME:-~/.local/share}/yoi/plugins/` or `<workspace>/.yoi/plugins/` are discovery only. Their presence is never permission to register Hooks or Tools, initialize WASM, start processes, open files, use network providers, read secrets, or launch MCP servers.
|
|
||||||
|
|
||||||
Trust differs by source, but none of the sources is self-authorizing:
|
|
||||||
|
|
||||||
- Builtin packages can be trusted as shipped code/data, but still require explicit enablement for a Worker/Profile when they affect runtime behavior.
|
|
||||||
- User packages are local user-installed artifacts and should be visible to workspaces, but they cannot bypass manifest/profile/tool/scope/secret policy.
|
|
||||||
- Project packages are repository-controlled artifacts and should be treated as untrusted until explicitly enabled by local policy. Cloning a repository must not be enough to execute a package.
|
|
||||||
|
|
||||||
## Identity and selector rules
|
|
||||||
|
|
||||||
Runtime identity is source-qualified: `builtin:<id>`, `user:<id>`, and `project:<id>` are distinct plugins even when `<id>` is the same string.
|
|
||||||
|
|
||||||
Durable enablement records should use source-qualified ids. Ambiguous unqualified ids fail closed. The implementation may offer convenience listing or search by bare id, but any operation that enables a package, grants permission, pins a digest, or records restored runtime state should require the fully qualified id.
|
|
||||||
|
|
||||||
Collision handling:
|
|
||||||
|
|
||||||
- Two packages with the same source-qualified id in the same effective store set are a discovery diagnostic and neither candidate is enabled implicitly.
|
|
||||||
- A `user:example` package does not override `builtin:example` unless a future explicit override rule says so.
|
|
||||||
- A `project:example` package does not override `user:example` or `builtin:example` by name alone.
|
|
||||||
|
|
||||||
## Discovery versus enablement
|
|
||||||
|
|
||||||
Discovery is a read-only inventory operation. It may report package metadata, validation errors, source, canonical store path, and deterministic digest. It must not initialize any runtime contribution.
|
|
||||||
|
|
||||||
Enablement is a resolved runtime plan. It should come from Profile/manifest configuration or another explicit local policy layer, then be recorded into the resolved Manifest/session metadata used to start the Worker. Restored Workers should use that resolved enabled-plugin plan instead of silently re-running fresh discovery and picking newer packages. Fresh discovery must not silently upgrade a restored Worker.
|
|
||||||
|
|
||||||
A minimal implemented enablement record is shaped like this. `version` is an exact package-version requirement; richer range constraints are deferred. `digest` is optional in authoring config, but fresh startup records the resolved digest into runtime metadata.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[plugins.enabled]]
|
|
||||||
id = "user:example"
|
|
||||||
version = "0.1.0" # optional exact package-version requirement
|
|
||||||
digest = "sha256:..." # optional pin in authoring, resolved in runtime metadata
|
|
||||||
config = { level = "concise" }
|
|
||||||
```
|
|
||||||
|
|
||||||
If no digest is pinned in authoring, fresh startup may resolve the newest acceptable discovered package according to explicit policy. Once a Worker is started, the resolved manifest/session metadata should record the exact source-qualified id and digest so restore is stable.
|
|
||||||
|
|
||||||
## Permissions and grants
|
|
||||||
|
|
||||||
Plugin permission declarations are requests, not grants. Effective grants are the result of Plugin-layer policy combined with existing Yoi authority layers:
|
|
||||||
|
|
||||||
- resolved manifest/profile plugin enablement;
|
|
||||||
- Plugin policy for the source-qualified package id and deterministic digest;
|
|
||||||
- normal tool permission policy;
|
|
||||||
- filesystem scope checks;
|
|
||||||
- web provider enablement and network safety checks;
|
|
||||||
- secret references and secret-store policy;
|
|
||||||
- runtime limits for WASM or other execution engines.
|
|
||||||
|
|
||||||
The Plugin package permission model must not reuse `worker::feature` HostAuthority or grant concepts. The feature layer is an API/contribution substrate; it is not a security boundary for untrusted plugin packages. Plugin grants need their own explicit policy that can fail closed before a Hook, Tool, WASM host function, provider bridge, or external runtime is exposed.
|
|
||||||
|
|
||||||
When a package requests authority outside policy, diagnostics should explain the denied category and package identity without leaking raw secret values, environment contents, full private config, or large plugin-provided text.
|
|
||||||
|
|
||||||
## Archive safety and materialization
|
|
||||||
|
|
||||||
Archive handling should validate before runtime use:
|
|
||||||
|
|
||||||
- Reject absolute paths, `..`, empty segments, Windows drive prefixes, NUL bytes, duplicate normalized paths, and paths that normalize outside the package root.
|
|
||||||
- Reject symlinks, hardlinks, device files, special files, and entries that are not regular files or directories.
|
|
||||||
- Enforce bounded extraction: maximum archive size, maximum expanded size, maximum entry count, maximum per-file size, and a compression-ratio limit.
|
|
||||||
- Validate every manifest-referenced path against the normalized entry set.
|
|
||||||
- Decode text manifests as UTF-8 and bound diagnostic excerpts.
|
|
||||||
- Ignore or normalize archive metadata such as mtimes, owners, groups, and executable bits; these should not affect runtime authority.
|
|
||||||
|
|
||||||
After validation, compute a deterministic digest over the normalized materialized package, not over incidental ZIP ordering or timestamps. A stable digest input should include the format version, normalized relative path, file length, and file content hash for each regular file in sorted order.
|
|
||||||
|
|
||||||
Runtime should materialize packages into a digest-keyed cache, for example:
|
|
||||||
|
|
||||||
```text
|
|
||||||
<cache>/plugins/sha256-<hex>/
|
|
||||||
plugin.toml
|
|
||||||
module.wasm
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Initialization should read from the digest-keyed cache, not directly from the mutable user/workspace store. This makes restore, diagnostics, and lock/pin behavior reproducible.
|
|
||||||
|
|
||||||
Optional lock behavior can be added in a later Ticket:
|
|
||||||
|
|
||||||
- an authoring-time pin in Profile/manifest configuration;
|
|
||||||
- a workspace lock file recording source-qualified id, version, source store, digest, and selected package path;
|
|
||||||
- restore metadata that records the actual digest used by the Worker.
|
|
||||||
|
|
||||||
A lock or pin is selection authority, not execution authority. Enablement and grants are still required.
|
|
||||||
|
|
||||||
## Diagnostics
|
|
||||||
|
|
||||||
Diagnostics should be safe, bounded, and attributable:
|
|
||||||
|
|
||||||
- Include source-qualified id when available, source kind, validation phase, and digest when computed.
|
|
||||||
- Prefer canonical store-relative paths or redacted absolute paths; avoid dumping large path lists.
|
|
||||||
- Never print raw secret values, provider tokens, environment dumps, or plugin-supplied opaque payloads.
|
|
||||||
- Treat package metadata and README text as untrusted content when showing it to an LLM or UI.
|
|
||||||
- Report discovery errors without disabling unrelated valid packages.
|
|
||||||
|
|
||||||
## Runtime notes
|
|
||||||
|
|
||||||
Declarative hooks are data contributions. Loading a declarative hook still requires explicit package enablement. Hook text should enter the system through the normal Hook/Engine paths, preserving the rule that model-affecting inputs are committed to history before they affect context when applicable.
|
|
||||||
|
|
||||||
WASM packages should initialize only from the digest-keyed cache after enablement and grant resolution. The host should use a narrow ABI, bounded memory, fuel/time limits, bounded output, and explicit host functions. A WASM module must not inherit filesystem, network, tool, secret, process, or MCP authority from the package store path.
|
|
||||||
|
|
||||||
Tool contributions from plugins should pass through the normal ToolRegistry and permission checks. Plugin-provided schemas can describe arguments, but schema presence is not permission to execute a tool.
|
|
||||||
|
|
||||||
## MCP boundary
|
|
||||||
|
|
||||||
MCP remains a separate feature-backed integration and is out of the initial Plugin package runtime. A `.yoi-plugin` package must not launch an MCP server or imply MCP enablement.
|
|
||||||
|
|
||||||
A future MCP/plugin bridge would need its own Ticket covering external process authority, lifecycle, permission mapping, resource/prompt operations, diagnostics, and trust model. Until then, package metadata may mention compatibility for humans, but runtime packaging should ignore it.
|
|
||||||
|
|
||||||
## Follow-up implementation cuts
|
|
||||||
|
|
||||||
Good follow-up Tickets are intentionally separable:
|
|
||||||
|
|
||||||
1. Manifest/Profile plugin enablement schema and resolved-session metadata, including restore behavior and digest pins.
|
|
||||||
2. Package discovery for builtin, user, and project stores with source-qualified identity and collision diagnostics.
|
|
||||||
3. `.yoi-plugin` archive validation, deterministic digest computation, and digest-keyed cache materialization.
|
|
||||||
4. Plugin-layer permission policy that combines package requests with existing tool/scope/web/secret/runtime allowlists without using `worker::feature` HostAuthority concepts.
|
|
||||||
5. Declarative hook package loading from enabled, materialized packages.
|
|
||||||
6. WASM package ABI, initialization limits, host-function grants, and Tool/Hook contribution plumbing.
|
|
||||||
7. Optional lock-file or pin update workflow for reproducible fresh startup.
|
|
||||||
8. Future MCP/plugin bridge, only if explicitly approved as a separate design and implementation effort.
|
|
||||||
|
|
||||||
### Component Model Tool runtime
|
|
||||||
|
|
||||||
Tool packages may use WebAssembly Component Model runtime metadata:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[runtime]
|
|
||||||
kind = "wasm-component"
|
|
||||||
component = "plugin.component.wasm"
|
|
||||||
world = "yoi:plugin/tool@1.0.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
For new Rust Tool packages, the preferred authoring path is the first-party
|
|
||||||
`yoi-plugin-pdk` plus the embedded `resources/plugin/templates/rust-component-tool/`
|
|
||||||
starter. The template uses a checkout-local path dependency for development and
|
|
||||||
documents a future out-of-tree pinned git `rev` dependency pattern. Crates.io
|
|
||||||
publication, remote template fetching, and package authoring commands are not
|
|
||||||
part of the current package/runtime contract.
|
|
||||||
|
|
||||||
Legacy raw core-Wasm metadata remains documented only as a rejected migration diagnostic. Packages must not use `entry`/`abi`; discovery reports `kind = "wasm"` / `abi = "yoi-plugin-wasm-1"` packages as rejected without executing the artifact. Component execution still requires explicit package enablement, exact source/version/digest grants, and matching Tool/host API permissions.
|
|
||||||
|
|||||||
@@ -1,467 +1,45 @@
|
|||||||
# Plugin development
|
# Plugin package authoring
|
||||||
|
|
||||||
This guide is for building a Yoi Plugin outside the Yoi runtime codebase. It describes the current Plugin package shape, how to author a Tool Plugin, how to enable it in a workspace, and how to inspect/debug it.
|
Dynamic Worker Plugins are not currently installable or executable. This guide covers only the retained offline `.yoi-plugin` authoring format.
|
||||||
|
|
||||||
Yoi Plugins are intentionally explicit. The Plugin system is designed around the following host-side principles:
|
## Commands
|
||||||
|
|
||||||
- package discovery is inventory only; putting a package in `.yoi/plugins` does not enable, register, or execute it;
|
Every retained command uses an explicit local input or destination:
|
||||||
- a Profile/config entry must explicitly enable each Plugin package by source-qualified id, version, and digest;
|
|
||||||
- Plugin grants must allow each surface and host API before registration or execution can use it;
|
|
||||||
- Plugin code runs only through the configured sandbox runtime;
|
|
||||||
- Plugin packages do not inherit Worker workspace filesystem, network, environment, or Ticket authority;
|
|
||||||
- Tool calls and Tool results use the ordinary Yoi Tool/Engine history path;
|
|
||||||
- Plugin metadata, output, and diagnostics are untrusted unless Yoi host policy says otherwise.
|
|
||||||
|
|
||||||
## Design intent
|
```sh
|
||||||
|
yoi plugin new rust-component-tool ./example-plugin
|
||||||
Yoi's Plugin platform is meant to make extension behavior reviewable before it becomes model-visible. A Plugin package should answer four separate questions:
|
yoi plugin new rust-component-service ./example-service
|
||||||
|
yoi plugin check ./example-plugin
|
||||||
1. **What is this package?** `plugin.toml` declares identity, version, runtime, surfaces, requested permissions, and Tool schemas.
|
yoi plugin pack ./example-plugin --output ./example-plugin.yoi-plugin
|
||||||
2. **Is it enabled here?** Workspace/Profile config chooses exact package refs and pinned digests.
|
yoi plugin check ./example-plugin.yoi-plugin
|
||||||
3. **What may it do?** Plugin grants authorize Tool surfaces and host APIs such as `https` and `fs`.
|
|
||||||
4. **How does it interact with the model?** Tool schemas/results enter through ordinary ToolRegistry and Tool history paths.
|
|
||||||
|
|
||||||
Keep these layers separate when designing a Plugin. Do not make package discovery imply enablement. Do not make SDK/PDK convenience imply authority. Do not treat Rust helper APIs or host API wrappers as permission grants. The host always re-checks authority at registration/execution/API-call boundaries.
|
|
||||||
|
|
||||||
Yoi's preferred Plugin shapes are **Tool first** for request/response capabilities and **Service/Ingress** for host-dispatched inbound events. A good Tool Plugin has a narrow schema, deterministic input/output behavior, explicit side-effect metadata, and a minimal grant set. A Service Plugin should keep long-lived transport ownership in the host and react to bounded ingress events by returning output commands.
|
|
||||||
|
|
||||||
Component Model authoring is the supported path for Plugins. Legacy raw core-Wasm manifests (`kind = "wasm"` / `abi = "yoi-plugin-wasm-1"`) are retired and rejected by `yoi plugin check`, discovery, `list`, and `show`; use the Rust PDK/template and `kind = "wasm-component"` instead.
|
|
||||||
|
|
||||||
## Current status
|
|
||||||
|
|
||||||
Implemented foundation:
|
|
||||||
|
|
||||||
- package discovery from project/user Plugin stores;
|
|
||||||
- explicit enablement resolution;
|
|
||||||
- Tool surface registration;
|
|
||||||
- Plugin permission grants;
|
|
||||||
- Component Model Tool runtime;
|
|
||||||
- first-party Rust PDK helpers for Component Model Tool guests;
|
|
||||||
- embedded Rust Component Tool starter template;
|
|
||||||
- `https` and `fs` host APIs for Tool runtime;
|
|
||||||
- read-only `yoi plugin list/show` inspection;
|
|
||||||
- local first-party authoring commands: `yoi plugin new`, `yoi plugin check`, and `yoi plugin pack`.
|
|
||||||
|
|
||||||
Still intentionally limited or separate from this guide:
|
|
||||||
|
|
||||||
- multi-language SDK/PDK crates;
|
|
||||||
- Service / Ingress surfaces, where the host owns transport lifecycle, dispatches bounded ingress events, and consumes output commands such as `websocket_send`;
|
|
||||||
- WebSocket or inbound HTTP for bidirectional external event integrations;
|
|
||||||
- public registry/install/update/signature tooling.
|
|
||||||
|
|
||||||
## Package locations
|
|
||||||
|
|
||||||
Yoi discovers `.yoi-plugin` packages from:
|
|
||||||
|
|
||||||
```text
|
|
||||||
<workspace>/.yoi/plugins/*.yoi-plugin
|
|
||||||
${XDG_DATA_HOME:-~/.local/share}/yoi/plugins/*.yoi-plugin
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Use project packages for workspace-specific Plugins and user packages for personal reusable Plugins. Project packages should normally be committed only when the package content is safe and intended to be part of the project.
|
- `new` writes an embedded starter template to the named destination and refuses unsafe or non-empty destinations.
|
||||||
|
- `check` parses and validates the named directory or package without executing Plugin code.
|
||||||
|
- `pack` validates the named directory and writes a deterministic constrained archive.
|
||||||
|
|
||||||
## Package archive format
|
`list`, `show`, `--workspace`, and `--profile` are intentionally unavailable. They previously implied ambient Workspace/user catalog discovery.
|
||||||
|
|
||||||
A `.yoi-plugin` package is currently a bounded ZIP archive. For now, create it with stored entries, not compressed entries:
|
## Safety and authority
|
||||||
|
|
||||||
```bash
|
Offline package commands do not:
|
||||||
(cd my-plugin && zip -0 -r ../example.echo.yoi-plugin plugin.toml plugin.component.wasm)
|
|
||||||
```
|
|
||||||
|
|
||||||
The archive root must contain `plugin.toml`. Runtime files referenced by the manifest must also be inside the archive. Yoi rejects path traversal, root escapes, malformed manifests, unsupported API/runtime versions, and other unsafe archive shapes.
|
- inspect repository or ancestor `.yoi/plugins` directories;
|
||||||
|
- inspect a user-data Plugin store;
|
||||||
|
- enable or install a package;
|
||||||
|
- mutate Profile or Manifest configuration;
|
||||||
|
- register Worker Tools, Services, or Ingress handlers;
|
||||||
|
- instantiate or execute a WASM component; or
|
||||||
|
- grant filesystem, network, secret, Ticket, or Workspace authority.
|
||||||
|
|
||||||
## Authoring CLI
|
`plugins` and `feature.plugins` are rejected by current Worker Manifest/Profile resolution. Statically compiled built-in Features are the only current Worker capability source.
|
||||||
|
|
||||||
Use the local authoring commands for first-party deterministic authoring. These commands never fetch remote templates, never run Plugin code, never mutate enablement configuration, and never generate or embed secrets.
|
## Package format
|
||||||
|
|
||||||
Create a Rust Component Tool starter from embedded resources:
|
A package directory contains `plugin.toml` plus the files named by that manifest. A packed `.yoi-plugin` uses the constrained deterministic archive format documented in [`../design/plugin-packages.md`](../design/plugin-packages.md). Validation rejects malformed metadata, unsafe paths, links, unsupported entries, bounds violations, digest inconsistencies, and legacy raw core-Wasm runtime declarations.
|
||||||
|
|
||||||
```bash
|
Generated templates include a placeholder `plugin.component.wasm`. Replace it with a real built component before `check` can report the package as verified. A verified package is still only an offline artifact; verification does not install or authorize it.
|
||||||
yoi plugin new rust-component-tool ./my-plugin
|
|
||||||
# or, for a host-dispatched Service/Ingress example:
|
|
||||||
yoi plugin new rust-component-service ./my-service-plugin
|
|
||||||
```
|
|
||||||
|
|
||||||
`new` writes only inside the requested destination and refuses an existing non-empty destination or destination symlink. The generated template includes `plugin.toml`, Rust source, Cargo metadata, README next steps, and a placeholder `plugin.component.wasm` artifact so local `check`/`pack` validation can run immediately. Replace the placeholder with a real built component before enabling or executing the Plugin.
|
## Future Server Plugin platform
|
||||||
|
|
||||||
Validate a source directory or an existing `.yoi-plugin` archive:
|
Do not copy packages into repository or user-data catalogs. Future installation must go through Server-owned package/artifact authority, immutable identity and digest selection, Backend-authored Worker execution plans, Runtime verification, and sandboxed execution. That platform is separate work and must not reintroduce filesystem catalog fallback.
|
||||||
|
|
||||||
```bash
|
|
||||||
yoi plugin check ./my-plugin
|
|
||||||
yoi plugin check ./my-plugin --json
|
|
||||||
yoi plugin check ./my-plugin.yoi-plugin --json
|
|
||||||
```
|
|
||||||
|
|
||||||
`check` performs bounded static validation of the directory/archive shape, manifest, runtime declaration, referenced artifact presence, Tool schemas, permission declarations, host API declarations, archive safety, and deterministic digest when a package can be materialized. Component-world validation is metadata-only: it verifies the declared world string and runtime manifest shape, but it does not instantiate or execute the component. A generated placeholder component produces `status = "partial"` plus a diagnostic and is not enablement-ready until replaced. Invalid checks print the same structured report and exit non-zero.
|
|
||||||
|
|
||||||
Pack a source directory into a deterministic stored `.yoi-plugin` archive:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yoi plugin pack ./my-plugin
|
|
||||||
yoi plugin pack ./my-plugin --output ./my-plugin.yoi-plugin --json
|
|
||||||
```
|
|
||||||
|
|
||||||
`pack` rejects malformed manifests, missing runtime artifacts, symlinks/root escapes, and unsupported package shapes. The JSON output contains the stable package reference, output path, digest, entries, and safety flags. After review, copy the package to `.yoi/plugins/` (or the user Plugin store) and add explicit Profile/config enablement with pinned digest and grants; packing and checking do not do this for you.
|
|
||||||
|
|
||||||
## Designing a Plugin
|
|
||||||
|
|
||||||
Design a Plugin around the smallest reviewable contract that is useful to the model.
|
|
||||||
|
|
||||||
For Tool Plugins:
|
|
||||||
|
|
||||||
- expose one clear operation per Tool name;
|
|
||||||
- keep the input schema narrow and explicit;
|
|
||||||
- make side effects visible in the Tool name, description, and `external_write` / permission metadata;
|
|
||||||
- request only the host APIs needed for that Tool;
|
|
||||||
- prefer deterministic, structured output over conversational prose;
|
|
||||||
- return bounded summaries and content that are useful as Tool results;
|
|
||||||
- avoid hiding long workflows, background daemons, or inbound event handling inside a Tool call.
|
|
||||||
|
|
||||||
A Tool should be a capability the model may choose to call, not a second agent runtime. If the desired behavior needs a long-lived connection, incoming events, or autonomous routing, put the transport lifecycle behind a Service/Ingress surface and let the host dispatch bounded events; do not stretch the Tool surface into a hidden polling loop.
|
|
||||||
|
|
||||||
Design package permissions as a review surface. A reviewer should be able to read `plugin.toml` plus the enablement grants and understand:
|
|
||||||
|
|
||||||
- what Tools become model-visible;
|
|
||||||
- what external side effects are possible;
|
|
||||||
- what hosts or paths can be touched;
|
|
||||||
- what data can flow back into ordinary Tool results.
|
|
||||||
|
|
||||||
## Manifest: `plugin.toml`
|
|
||||||
|
|
||||||
A minimal Component Model Tool Plugin manifest looks like this:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
schema_version = 1
|
|
||||||
id = "example.echo"
|
|
||||||
name = "Example Echo"
|
|
||||||
version = "0.1.0"
|
|
||||||
surfaces = ["tool"]
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "tool" },
|
|
||||||
{ kind = "tool", name = "example_echo" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[runtime]
|
|
||||||
kind = "wasm-component"
|
|
||||||
component = "plugin.component.wasm"
|
|
||||||
world = "yoi:plugin/tool@1.0.0"
|
|
||||||
|
|
||||||
[[tools]]
|
|
||||||
name = "example_echo"
|
|
||||||
description = "Echo input text."
|
|
||||||
input_schema = { type = "object", properties = { text = { type = "string" } }, required = ["text"], additionalProperties = false }
|
|
||||||
external_write = false
|
|
||||||
```
|
|
||||||
|
|
||||||
`wasm-component` is the public runtime kind. Legacy raw core-Wasm declarations such as `kind = "wasm"` / `abi = "yoi-plugin-wasm-1"` are no longer compatibility paths: static validation rejects them with a bounded diagnostic and they are not displayed as active/eligible Plugins.
|
|
||||||
|
|
||||||
Do not rely on package presence to activate anything. Discovery only records inventory.
|
|
||||||
|
|
||||||
## Rust PDK authoring
|
|
||||||
|
|
||||||
Rust authoring with `yoi-plugin-pdk` is the supported path for new Tool Plugins. Raw core-Wasm ABI packages are retired and should be rewritten as Component Model packages before enabling.
|
|
||||||
|
|
||||||
Create a starter with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yoi plugin new rust-component-tool ./my-plugin
|
|
||||||
# or, for a host-dispatched Service/Ingress example:
|
|
||||||
yoi plugin new rust-component-service ./my-service-plugin
|
|
||||||
```
|
|
||||||
|
|
||||||
The generated package contains:
|
|
||||||
|
|
||||||
- `Cargo.toml` with a checkout-local `yoi-plugin-pdk` path dependency;
|
|
||||||
- `src/lib.rs` with the runtime binding setup and typed JSON Tool handling;
|
|
||||||
- `plugin.toml` targeting `kind = "wasm-component"`;
|
|
||||||
- README next steps and the out-of-tree pinned git `rev` dependency pattern.
|
|
||||||
|
|
||||||
For an independent Plugin repository, replace the checkout-local path dependency with a pinned Yoi source revision. Use the repository root `.git` URL, not the browser `/src/branch/...` URL, and pin `rev` instead of tracking a moving branch:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
yoi-plugin-pdk = { git = "https://gitea.hareworks.net/Hare/yoi.git", package = "yoi-plugin-pdk", rev = "<pinned-yoi-commit-sha>" }
|
|
||||||
```
|
|
||||||
|
|
||||||
As a Plugin author, treat the generated binding setup as template code. Edit the typed input/output structs and handler function rather than hand-writing runtime ABI glue.
|
|
||||||
|
|
||||||
The important authoring shape is:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use yoi_plugin_pdk::{ToolContext, ToolError, ToolOutput};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct EchoInput {
|
|
||||||
text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct EchoOutput<'a> {
|
|
||||||
tool: &'a str,
|
|
||||||
text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_echo(ctx: ToolContext, input: EchoInput) -> Result<ToolOutput, ToolError> {
|
|
||||||
ToolOutput::json(
|
|
||||||
format!("{} ok", ctx.tool_name()),
|
|
||||||
EchoOutput {
|
|
||||||
tool: ctx.tool_name(),
|
|
||||||
text: input.text,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
yoi_plugin_pdk::export_component_tool!(Plugin, handle_echo);
|
|
||||||
```
|
|
||||||
|
|
||||||
The PDK parses the runtime input string into a typed Rust value, passes a `ToolContext` containing the selected Tool name, and serializes `ToolOutput` JSON accepted by the current component runtime. `ToolError` values are structured and bounded, then rendered through the ordinary Tool result path; the component cannot inject hidden context.
|
|
||||||
|
|
||||||
The PDK is guest-side only. It does not depend on Yoi host/runtime crates and does not grant filesystem, network, or environment authority. Host-side Plugin manifests and explicit enablement grants remain the authority boundary for Tool execution and for host APIs such as `https` and `fs`.
|
|
||||||
|
|
||||||
The expected authoring flow is Rust-first: generate the starter, edit `src/lib.rs`, replace the local path dependency with a pinned `git` + `rev` dependency when the Plugin lives outside the Yoi checkout, build the Rust component artifact for `plugin.component.wasm`, run `yoi plugin check`, then `yoi plugin pack`. Crates.io publication and remote template fetching are intentionally deferred. Use `yoi plugin list/show` to inspect the packaged/enabled state before trying to execute the Tool.
|
|
||||||
|
|
||||||
## Enabling a Plugin in a workspace
|
|
||||||
|
|
||||||
Enablement belongs in the resolved Profile/config path for the workspace. Add it to the project Profile source selected by the Backend-managed, revisioned Workspace Virtual Config. Ambient files below the Workdir are not a Profile override layer and are not read when the Worker starts. Keep raw secrets and machine-local paths out of the Profile; refer to separately managed secrets where a capability supports them.
|
|
||||||
|
|
||||||
The following TOML shows the equivalent low-level Profile/config artifact shape; it is not an ambient workspace override file:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[feature.plugins]
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[[plugins.enabled]]
|
|
||||||
id = "project:example.echo"
|
|
||||||
version = "0.1.0"
|
|
||||||
digest = "sha256:<digest from yoi plugin show/list>"
|
|
||||||
surfaces = ["tool"]
|
|
||||||
|
|
||||||
[plugins.enabled.grants]
|
|
||||||
id = "project:example.echo"
|
|
||||||
version = "0.1.0"
|
|
||||||
digest = "sha256:<same digest>"
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "tool" },
|
|
||||||
{ kind = "tool", name = "example_echo" },
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
A source-qualified id is preferred:
|
|
||||||
|
|
||||||
```text
|
|
||||||
project:example.echo
|
|
||||||
user:example.echo
|
|
||||||
builtin:example.echo
|
|
||||||
```
|
|
||||||
|
|
||||||
Unqualified ids can be ambiguous and should fail closed when more than one source matches.
|
|
||||||
|
|
||||||
## Inspecting Plugins
|
|
||||||
|
|
||||||
Use the read-only CLI inspection commands first:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yoi plugin list
|
|
||||||
yoi plugin list --json
|
|
||||||
yoi plugin show project:example.echo
|
|
||||||
yoi plugin show project:example.echo --json
|
|
||||||
```
|
|
||||||
|
|
||||||
`list/show` must not execute Plugin code. They are intended to explain static state:
|
|
||||||
|
|
||||||
- discovered packages;
|
|
||||||
- enabled vs disabled packages;
|
|
||||||
- missing packages referenced by enablement;
|
|
||||||
- invalid manifests;
|
|
||||||
- digest/version/source mismatches;
|
|
||||||
- granted/denied permissions;
|
|
||||||
- Tool registration eligibility;
|
|
||||||
- runtime metadata.
|
|
||||||
|
|
||||||
Typical statuses:
|
|
||||||
|
|
||||||
```text
|
|
||||||
active enabled and statically valid for at least one surface/tool
|
|
||||||
disabled discovered but not explicitly enabled
|
|
||||||
missing enablement references a package that is not discovered
|
|
||||||
rejected invalid manifest, incompatible API, digest mismatch, grant denial, etc.
|
|
||||||
partial usable package with some rejected surfaces/tools
|
|
||||||
```
|
|
||||||
|
|
||||||
## `request` host API
|
|
||||||
|
|
||||||
The `request` host API is a one-shot outbound HTTP request API. It is meant for bounded Tool calls such as JSON POSTs or REST requests. It is not a WebSocket, SSE/event-stream, gateway, daemon, or inbound HTTP surface; persistent transports require a separate Plugin capability.
|
|
||||||
|
|
||||||
Manifest permissions should request `host_api.request` in addition to the Tool permissions, and the package manifest must statically declare the URL targets it may call. Enablement grants must then allow the API and grant matching request targets. A grant without a matching manifest target is unsafe/unused and is shown as ineligible rather than expanding authority.
|
|
||||||
|
|
||||||
Example manifest shape:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "tool" },
|
|
||||||
{ kind = "tool", name = "http_post_json" },
|
|
||||||
{ kind = "host_api", api = "request" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[request]]
|
|
||||||
scheme = "https"
|
|
||||||
host = "api.example.com"
|
|
||||||
methods = ["POST"]
|
|
||||||
path_prefixes = ["/v1/"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Example enablement grant shape:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[plugins.enabled.grants]
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "tool" },
|
|
||||||
{ kind = "tool", name = "http_post_json" },
|
|
||||||
{ kind = "host_api", api = "request" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[plugins.enabled.grants.request]]
|
|
||||||
scheme = "https"
|
|
||||||
host = "api.example.com"
|
|
||||||
methods = ["POST"]
|
|
||||||
path_prefixes = ["/v1/"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Yoi checks method, scheme, host, optional port, and path prefix against both the manifest declaration and enablement grant before any network I/O. `http://localhost`, loopback, private, and other local targets are never ambient; they require an explicit manifest request target and an explicit matching grant. The explicit request target is the declared URL authority; a granted DNS hostname may resolve to a loopback/private address without requiring a separate literal-IP grant, so reviewers should grant hostnames only when that resolution behavior is intended. Broad targets such as `host = "*"` are supported only as visibly broad request permissions in inspection/diagnostics. Embedded credentials, credential-like headers, oversize requests/responses, WebSocket URLs/upgrades, and SSE/event-stream requests are rejected.
|
|
||||||
|
|
||||||
## Service ingress and output commands
|
|
||||||
|
|
||||||
Service Plugins export the `yoi:plugin/instance@1.0.0` world. The host starts one Plugin instance, owns external ingress transports, and calls `handle_ingress(name, event_json)` with bounded event envelopes. A WebSocket ingress event contains fields such as `kind`, `source`, `ingress_name`, `payload`, `created_at`, `attempt`, and `correlation_id`; the Rust PDK maps this to `PluginIngressEvent`.
|
|
||||||
|
|
||||||
Service handlers return `ServiceOutput`, not ordinary ToolOutput. Side effects are requested through top-level `output_commands`. For a WebSocket reply, use the PDK helper:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
ServiceOutput::websocket_send(
|
|
||||||
&event,
|
|
||||||
"reply-1",
|
|
||||||
event.source.strip_prefix("websocket:").unwrap_or(&event.source),
|
|
||||||
"pong",
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
This serializes a `websocket_send` command with `source_event_id`, `command_id`, `payload.url`, `payload.text`, and a request timestamp. The host parses, bounds, grant-checks, and dispatches the command through the host-owned WebSocket driver. Do not create a long-running guest receive loop for Service integrations; incoming messages should arrive as ingress events.
|
|
||||||
|
|
||||||
A minimal manifest shape is:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
surfaces = ["tool", "service", "ingress"]
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "service" },
|
|
||||||
{ kind = "service", name = "example_service" },
|
|
||||||
{ kind = "surface", surface = "ingress" },
|
|
||||||
{ kind = "ingress", name = "example_ws" },
|
|
||||||
{ kind = "host_api", api = "websocket" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[runtime]
|
|
||||||
kind = "wasm-component"
|
|
||||||
world = "yoi:plugin/instance@1.0.0"
|
|
||||||
component = "plugin.component.wasm"
|
|
||||||
|
|
||||||
[[services]]
|
|
||||||
name = "example_service"
|
|
||||||
description = "Host-managed service instance."
|
|
||||||
lifecycle = "host-managed"
|
|
||||||
|
|
||||||
[[ingresses]]
|
|
||||||
name = "example_ws"
|
|
||||||
description = "Handles host-owned WebSocket text events."
|
|
||||||
event_kinds = ["websocket_text", "websocket_close", "websocket_error"]
|
|
||||||
sources = ["websocket:wss://gateway.example.com/gateway"]
|
|
||||||
input_schema = { type = "object" }
|
|
||||||
|
|
||||||
[[websocket]]
|
|
||||||
scheme = "wss"
|
|
||||||
host = "gateway.example.com"
|
|
||||||
path_prefixes = ["/gateway"]
|
|
||||||
```
|
|
||||||
|
|
||||||
The `host_api.websocket` permission and `[[websocket]]` target are required for `websocket_send` output commands. Runtime enablement grants must explicitly allow the same WebSocket target; the manifest declaration alone is not authority.
|
|
||||||
|
|
||||||
Generate a fuller example with `yoi plugin new rust-component-service ./my-service-plugin`.
|
|
||||||
|
|
||||||
## `websocket` host API
|
|
||||||
|
|
||||||
The `websocket` host API is a separate grant-gated capability named `host_api.websocket`, not an extension of `host_api.request`. It opens host-owned WebSocket connections only when both the package manifest and enablement config declare matching targets. Tool-style/internal bounded use can still drive the lifecycle explicitly through `open`, `send-text`, `recv`, and `close`; incoming messages are returned only from bounded `recv` calls and are not injected into model context, history, Dashboard state, or Ticket state. Service Plugins should prefer the host-owned Service WebSocket driver instead of running a long-lived guest recv loop: declare a Service ingress source as `websocket:wss://host/path`, include the `websocket_text`/`websocket_close`/`websocket_error` event kinds you want delivered, and emit the Service output command `websocket_send` to send text back through the same grant-checked host connection.
|
|
||||||
|
|
||||||
Example manifest shape:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "tool" },
|
|
||||||
{ kind = "tool", name = "gateway_step" },
|
|
||||||
{ kind = "host_api", api = "websocket" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[websocket]]
|
|
||||||
scheme = "wss"
|
|
||||||
host = "gateway.example.com"
|
|
||||||
path_prefixes = ["/gateway"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Example enablement grant shape:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[plugins.enabled.grants]
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "tool" },
|
|
||||||
{ kind = "tool", name = "gateway_step" },
|
|
||||||
{ kind = "host_api", api = "websocket" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[plugins.enabled.grants.websocket]]
|
|
||||||
scheme = "wss"
|
|
||||||
host = "gateway.example.com"
|
|
||||||
path_prefixes = ["/gateway"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Yoi checks scheme (`ws`/`wss`), host, optional port, and path prefix against both declarations before opening the connection. Loopback/private/local targets are not ambient; they require explicit matching manifest and grant entries. Broad WebSocket targets such as `host = "*"` are reported as broad WebSocket diagnostics. v1 is text-only: `send-text` requires UTF-8, binary receive fails closed, guest-supplied handshake headers and embedded URL credentials are rejected, and SecretRef-based credential/header injection is future work. The host bounds open descriptors, text/message size, receive timeout, connection count, handle lifetime, and cleanup on close/instance stop/drop.
|
|
||||||
|
|
||||||
## `fs` host API
|
|
||||||
|
|
||||||
The `fs` host API is Plugin-scoped and grant-gated. Plugins do not inherit the Worker/workspace filesystem authority automatically.
|
|
||||||
|
|
||||||
Example grant shape:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[plugins.enabled.grants]
|
|
||||||
permissions = [
|
|
||||||
{ kind = "surface", surface = "tool" },
|
|
||||||
{ kind = "tool", name = "read_notes" },
|
|
||||||
{ kind = "host_api", api = "fs" },
|
|
||||||
]
|
|
||||||
|
|
||||||
[[plugins.enabled.grants.fs]]
|
|
||||||
root = "/absolute/path/to/plugin-data"
|
|
||||||
operations = ["read", "list"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Yoi normalizes paths, rejects `..` traversal, rejects symlink/root escapes, and applies read/write/list bounds. Diagnostics must not include file contents.
|
|
||||||
|
|
||||||
## Development checklist
|
|
||||||
|
|
||||||
1. Create a package directory with `plugin.toml` and the runtime artifact.
|
|
||||||
2. Build the Wasm/component artifact.
|
|
||||||
3. Package with stored ZIP entries as `.yoi-plugin`.
|
|
||||||
4. Put it under `.yoi/plugins/` or the user Plugin store.
|
|
||||||
5. Run `yoi plugin list` and `yoi plugin show <ref>`.
|
|
||||||
6. Add explicit enablement and grants.
|
|
||||||
7. Re-run `yoi plugin show <ref>` until status/diagnostics are correct.
|
|
||||||
8. Start Yoi with `features.plugins = true` in the resolved config/Profile.
|
|
||||||
9. Call the Tool and verify ordinary Tool result/history behavior.
|
|
||||||
|
|
||||||
## Safety rules for Plugin authors
|
|
||||||
|
|
||||||
- Do not assume ambient filesystem, network, or environment access.
|
|
||||||
- Do not put secrets in `plugin.toml` or package files.
|
|
||||||
- Request only the minimal host APIs and grants needed.
|
|
||||||
- Keep Tool output bounded and structured.
|
|
||||||
- Prefer Component Model authoring for new Plugins.
|
|
||||||
- Treat raw core-Wasm ABI support as retired; migration diagnostics may mention it, but authors should publish `wasm-component` packages.
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
# Rust Service Plugin Template
|
# Rust Component Model instance Plugin template
|
||||||
|
|
||||||
This template targets the Component Model-only runtime (`runtime.kind = "wasm-component"`) and exports the `yoi:plugin/instance@1.0.0` world.
|
This offline authoring template declares the proposed `yoi:plugin/instance@1.0.0` world. Yoi currently provides no Plugin installation or Worker execution path.
|
||||||
|
|
||||||
It demonstrates both authoring surfaces supported by a shared Plugin instance:
|
The example contains request/response Tool and Service ingress shapes that a future sandboxed Server Plugin platform may support. Their host API and grant metadata is inert in the current product and grants no authority.
|
||||||
|
|
||||||
- `example_echo` is an ordinary request/response Tool handler.
|
Build with `cargo component build --release` (or the project-specific build command used by your package), then run `yoi plugin check .` and `yoi plugin pack .` against the explicit directory. Passing offline validation does not install or authorize the package.
|
||||||
- `example_ws` is a Service ingress handler. The host owns WebSocket receive/reconnect work and dispatches bounded `websocket_text` events into `handle_ingress`. The guest replies by returning a `websocket_send` output command in `ServiceOutput`; do not run a guest-side `recv(timeout)` polling loop. The manifest declares `host_api.websocket` plus a matching `[[websocket]]` target for the example URL. Enablement grants must explicitly allow the same WebSocket target before the host will send output commands.
|
|
||||||
|
|
||||||
Build with `cargo component build --release` (or the project-specific build command used by your Plugin packaging flow), then run `yoi plugin check` / `yoi plugin pack` from the generated Plugin directory.
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ This is the embedded starter template for a Yoi Component Model Tool Plugin writ
|
|||||||
|
|
||||||
## What this template demonstrates
|
## What this template demonstrates
|
||||||
|
|
||||||
- `wasm-component` runtime targeting `yoi:plugin/tool@1.0.0`.
|
- Offline package metadata targeting the proposed `yoi:plugin/tool@1.0.0` world.
|
||||||
- Guest-side runtime binding setup through the PDK.
|
- Guest-side runtime binding setup through the PDK.
|
||||||
- Typed JSON input parsing through `run_json_tool` via `export_component_tool!`.
|
- Typed JSON input parsing through `run_json_tool` via `export_component_tool!`.
|
||||||
- Typed JSON output serialization with `ToolOutput::json`.
|
- Typed JSON output serialization with `ToolOutput::json`.
|
||||||
- Structured, bounded `ToolError` output for user-visible Tool failures.
|
- Structured, bounded `ToolError` output for user-visible Tool failures.
|
||||||
|
|
||||||
The PDK is guest-side only. It does not grant filesystem, network, or environment authority. Host-side Plugin manifests and grants remain the authority boundary for Tool execution and host APIs.
|
The PDK is guest-side only. It does not grant filesystem, network, or environment authority. Yoi currently provides no Plugin installation or Worker execution path; this template is retained for offline package authoring.
|
||||||
|
|
||||||
## Checkout/development dependency
|
## Checkout/development dependency
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ If this template is copied to an independent Plugin repository, pin a Yoi source
|
|||||||
yoi-plugin-pdk = { git = "https://gitea.hareworks.net/Hare/yoi.git", package = "yoi-plugin-pdk", rev = "<pinned-yoi-commit-sha>" }
|
yoi-plugin-pdk = { git = "https://gitea.hareworks.net/Hare/yoi.git", package = "yoi-plugin-pdk", rev = "<pinned-yoi-commit-sha>" }
|
||||||
```
|
```
|
||||||
|
|
||||||
`plugin.component.wasm` in the template is a text placeholder so `yoi plugin check` and `yoi plugin pack` can exercise deterministic local package validation immediately. Replace it with a real built component before enabling or executing the Plugin.
|
`plugin.component.wasm` in the template is a text placeholder so `yoi plugin check` and `yoi plugin pack` can exercise deterministic local package validation immediately. Replace it with a real built component before treating the offline package as verified.
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
@@ -34,4 +34,4 @@ yoi-plugin-pdk = { git = "https://gitea.hareworks.net/Hare/yoi.git", package = "
|
|||||||
2. Replace `EchoInput` / `EchoOutput` and `handle_echo` with your Tool logic.
|
2. Replace `EchoInput` / `EchoOutput` and `handle_echo` with your Tool logic.
|
||||||
3. Build the Rust component artifact for `wasm32-unknown-unknown`, replacing the placeholder `plugin.component.wasm`.
|
3. Build the Rust component artifact for `wasm32-unknown-unknown`, replacing the placeholder `plugin.component.wasm`.
|
||||||
4. Run `yoi plugin check .` and `yoi plugin pack . --output ./my-plugin.yoi-plugin`.
|
4. Run `yoi plugin check .` and `yoi plugin pack . --output ./my-plugin.yoi-plugin`.
|
||||||
5. Copy the package to a Plugin store and add explicit enablement with pinned digest/grants after review.
|
5. Retain the package as an offline artifact. Yoi does not currently install or execute it.
|
||||||
|
|||||||
@@ -29,6 +29,5 @@ import "./base.dcdl" // {
|
|||||||
complete = false;
|
complete = false;
|
||||||
};
|
};
|
||||||
orchestration = { enabled = false; };
|
orchestration = { enabled = false; };
|
||||||
plugins = { enabled = false; };
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user