diff --git a/.yoi/tickets/00001KVHKWNQA/artifacts/.gitkeep b/.yoi/tickets/00001KVHKWNQA/artifacts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.yoi/tickets/00001KVHKWNQA/artifacts/relations.json b/.yoi/tickets/00001KVHKWNQA/artifacts/relations.json new file mode 100644 index 00000000..977b3acf --- /dev/null +++ b/.yoi/tickets/00001KVHKWNQA/artifacts/relations.json @@ -0,0 +1,37 @@ +{ + "version": 1, + "relations": [ + { + "ticket_id": "00001KVHKWNQA", + "kind": "depends_on", + "target": "00001KVG0HR96", + "note": "PDK targets the Component Model Tool runtime.", + "author": "yoi ticket", + "at": "2026-06-20T04:17:24Z" + }, + { + "ticket_id": "00001KVHKWNQA", + "kind": "related", + "target": "00001KVFD3YSV", + "note": "PDK authoring flow should pair with read-only plugin inspection.", + "author": "yoi ticket", + "at": "2026-06-20T04:17:24Z" + }, + { + "ticket_id": "00001KVHKWNQA", + "kind": "related", + "target": "00001KVFDX9AF", + "note": "PDK should later wrap https host API ergonomically.", + "author": "yoi ticket", + "at": "2026-06-20T04:17:24Z" + }, + { + "ticket_id": "00001KVHKWNQA", + "kind": "related", + "target": "00001KVFDX9AY", + "note": "PDK should later wrap fs host API ergonomically.", + "author": "yoi ticket", + "at": "2026-06-20T04:17:24Z" + } + ] +} diff --git a/.yoi/tickets/00001KVHKWNQA/item.md b/.yoi/tickets/00001KVHKWNQA/item.md new file mode 100644 index 00000000..324ea2f9 --- /dev/null +++ b/.yoi/tickets/00001KVHKWNQA/item.md @@ -0,0 +1,88 @@ +--- +title: 'Plugin: add Rust PDK and embedded authoring templates for Component Model Tools' +state: 'ready' +created_at: '2026-06-20T04:16:14Z' +updated_at: '2026-06-20T04:17:24Z' +assignee: null +readiness: 'implementation_ready' +risk_flags: ['plugin', 'pdk', 'component-model', 'authoring', 'templates', 'sdk', 'no-crates-io'] +--- + +## Background + +Yoi can now discover Plugin packages, register Tool surfaces, execute sandboxed WASM/Component Model Tool Plugins, enforce Plugin grants, expose `https` / `fs` host APIs, and inspect Plugin state through CLI. The next gap is authoring: independent Plugin developers should not need to write raw WIT/ABI glue or copy ad-hoc examples by hand. + +This Ticket adds a first-party Rust PDK for Component Model Tool Plugins and embeds a starter template into Yoi resources. It deliberately does not publish to crates.io yet and does not fetch remote templates. The PDK/API and WIT world are still young, so out-of-tree authors should initially use a git rev or generated local path dependency rather than depending on a public semver crate. + +## Requirements + +- Add a Rust PDK crate inside the Yoi workspace. + - Suggested package name: `yoi-plugin-pdk`. + - Suggested path: `crates/plugin-pdk`. + - Target Component Model Tool Plugins, not raw core-Wasm as the primary authoring path. +- PDK must be guest-side only. + - Do not depend on host runtime crates such as `pod`, `llm-worker`, `tui`, or `client`. + - Keep dependencies minimal, e.g. `serde`, `serde_json`, and WIT binding support. + - Do not expose ambient fs/network/env authority. +- Provide ergonomic Tool helpers. + - Typed JSON input parsing. + - Typed JSON output serialization. + - Structured `ToolError` / error-code helpers. + - `ToolContext` containing at least the tool name. + - Helper equivalent to `run_json_tool` that returns the ToolOutput JSON expected by the Yoi runtime. +- Provide Component Model binding glue. + - Re-export or wrap generated WIT bindings enough that an author does not need to hand-write raw pointer/length ABI code. + - Prefer a minimal `Guest` impl helper first; add a macro only if it is simpler and testable. +- Include embedded authoring templates under runtime resources. + - Suggested path: `resources/plugin/templates/rust-component-tool/`. + - Template includes `Cargo.toml`, `src/lib.rs`, `plugin.toml`, and a short README. + - Template should be usable by a future `yoi plugin new` command without network access. +- Template dependency policy: + - In checkout/dev mode, template may use a local path dependency to `crates/plugin-pdk`. + - For out-of-tree authors, docs/template comments should show a git `rev` dependency pattern. + - Do not publish or require crates.io for this Ticket. + - Do not use `curl | sh` or remote template fetch. +- Include at least one example or fixture Plugin using the PDK. + - Echo-style Component Model Tool is enough. + - It should compile to a component artifact through the documented toolchain or, if full component build automation is not yet available, be covered by a clear fixture/test boundary. +- Update Plugin development docs. + - Explain that Component Model + PDK is the preferred authoring path. + - Explain that raw core-Wasm ABI is compatibility/transitional. + - Explain why crates.io publication and remote templates are intentionally deferred. + +## Acceptance criteria + +- Workspace contains a guest-side `yoi-plugin-pdk` crate. +- A Rust Component Model Tool Plugin can use the PDK to implement a JSON Tool without raw pointer/length ABI code. +- PDK-produced Tool success output is accepted by the existing Yoi Plugin Tool runtime. +- PDK-produced Tool errors are bounded and represented as ordinary Tool result/error content. +- PDK does not grant or imply authority; host-side Plugin grants still decide `https` / `fs` / Tool execution access. +- Embedded `rust-component-tool` template exists in runtime resources and is suitable for future `yoi plugin new` expansion. +- No crates.io publication is required or performed. +- No remote template fetch is implemented. +- Tests cover: + - PDK JSON input/output happy path; + - PDK error output path; + - PDK sample/template compiles or fixture is validated; + - sample Plugin can be executed by Yoi component runtime if feasible in current test infrastructure; + - PDK has no host-runtime crate dependency. +- Validation: focused PDK/plugin tests, `cargo fmt --check`, relevant `cargo check` / `cargo test`, `git diff --check`, and `nix build .#yoi` because workspace/package/resources may change. + +## Non-goals + +- Publishing `yoi-plugin-pdk` to crates.io. +- Remote template fetch or `curl | sh` setup. +- `yoi plugin new/check/pack` CLI implementation. +- Multi-language PDKs. +- Service / Ingress authoring. +- WebSocket / inbound HTTP bridge support. +- Replacing Plugin grants with PDK-side checks. + +## Related work + +- `00001KVG0HR9M` — Objective: Plugin platform roadmap. +- `00001KVG0HR96` — Plugin Component Model runtime. +- `00001KVFD3YSV` — Plugin read-only CLI inspection. +- `00001KVFDX9AF` — Plugin https host API. +- `00001KVFDX9AY` — Plugin fs host API. +- `docs/development/plugin-development.md` — current Plugin development guide. diff --git a/.yoi/tickets/00001KVHKWNQA/thread.md b/.yoi/tickets/00001KVHKWNQA/thread.md new file mode 100644 index 00000000..c6d34e33 --- /dev/null +++ b/.yoi/tickets/00001KVHKWNQA/thread.md @@ -0,0 +1,7 @@ + + +## 作成 + +LocalTicketBackend によって作成されました。 + +--- diff --git a/.yoi/tickets/00001KVHKWNQS/artifacts/.gitkeep b/.yoi/tickets/00001KVHKWNQS/artifacts/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.yoi/tickets/00001KVHKWNQS/artifacts/relations.json b/.yoi/tickets/00001KVHKWNQS/artifacts/relations.json new file mode 100644 index 00000000..d94f3ca1 --- /dev/null +++ b/.yoi/tickets/00001KVHKWNQS/artifacts/relations.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "relations": [ + { + "ticket_id": "00001KVHKWNQS", + "kind": "depends_on", + "target": "00001KVHKWNQA", + "note": "Authoring new/check/pack uses the Rust PDK and embedded templates.", + "author": "yoi ticket", + "at": "2026-06-20T04:17:24Z" + }, + { + "ticket_id": "00001KVHKWNQS", + "kind": "related", + "target": "00001KVFD3YSV", + "note": "Authoring check/pack diagnostics should align with plugin list/show inspection.", + "author": "yoi ticket", + "at": "2026-06-20T04:17:24Z" + }, + { + "ticket_id": "00001KVHKWNQS", + "kind": "related", + "target": "00001KVG0HR96", + "note": "Authoring CLI validates Component Model Tool package metadata.", + "author": "yoi ticket", + "at": "2026-06-20T04:17:24Z" + } + ] +} diff --git a/.yoi/tickets/00001KVHKWNQS/item.md b/.yoi/tickets/00001KVHKWNQS/item.md new file mode 100644 index 00000000..57aeea51 --- /dev/null +++ b/.yoi/tickets/00001KVHKWNQS/item.md @@ -0,0 +1,91 @@ +--- +title: 'Plugin: add authoring CLI new/check/pack' +state: 'ready' +created_at: '2026-06-20T04:16:14Z' +updated_at: '2026-06-20T04:17:24Z' +assignee: null +readiness: 'implementation_ready' +risk_flags: ['plugin', 'cli', 'authoring', 'templates', 'package-validation', 'packaging', 'read-only-check'] +--- + +## Background + +After the Rust PDK and embedded templates exist, independent Plugin developers need first-party CLI tooling to scaffold, validate, and package Plugins without relying on remote shell scripts or hand-written ZIP commands. + +This Ticket adds authoring commands for local Plugin development. It complements read-only operational inspection (`yoi plugin list/show`) but serves a different audience: Plugin authors preparing a package before enabling it in Yoi. + +## Requirements + +- Add Plugin authoring subcommands to the product CLI. + - `yoi plugin new rust-component-tool ` + - `yoi plugin check ` + - `yoi plugin pack [--output ]` +- `new` uses embedded templates only. + - No remote template fetch. + - No `curl | sh` flow. + - Generated files should include `Cargo.toml`, `src/lib.rs`, `plugin.toml`, and README/next-steps. + - Generated dependency should be appropriate for the current checkout/release mode: local path in checkout mode, or documented git rev/tag pattern if out-of-tree. +- `check` validates a Plugin directory or `.yoi-plugin` package without executing Plugin code. + - Parse `plugin.toml`. + - Validate package id/version/source-compatible shape. + - Validate runtime kind and referenced artifact presence. + - Validate Component Model world metadata where possible. + - Validate Tool schema shape. + - Validate requested permissions / host API declarations. + - Validate archive safety for packages: path traversal, root escape, unsupported compression, bounded file count/size. + - Calculate and print deterministic digest. + - Produce actionable diagnostics and a suggested enablement/grant snippet. +- `pack` creates a deterministic `.yoi-plugin` package. + - Include required manifest/runtime files. + - Use currently supported archive format, including stored entries if compression is not supported. + - Reject unsafe paths / root escapes. + - Print output path and digest. + - Do not modify workspace enablement config. +- Provide JSON output for automation where useful. + - At minimum `check --json` and `pack --json`. +- Keep commands safe. + - `check` and `pack` do not execute Plugin code. + - `new` only writes into the requested destination and refuses to overwrite non-empty directories unless an explicit safe option is added. + - No secrets are generated or embedded. +- Integrate with existing inspection language. + - Diagnostics/statuses should align with `yoi plugin list/show` where possible. + +## Acceptance criteria + +- `yoi plugin new rust-component-tool ./my-plugin` creates a usable template without network access. +- `yoi plugin check ./my-plugin` validates the generated template and reports next steps/digest/enablement guidance. +- `yoi plugin pack ./my-plugin` creates a `.yoi-plugin` package that Yoi discovery can read. +- `check` can validate an existing `.yoi-plugin` archive. +- `check --json` returns a stable typed report suitable for tests/agents. +- `pack --json` returns output path and digest. +- Unsafe package paths / traversal / unsupported compression / missing manifest / missing runtime artifact are rejected with clear diagnostics. +- Commands do not execute Plugin code or mutate enablement config. +- Tests cover: + - `new` generated file set; + - refusal to overwrite non-empty destination; + - `check` valid directory; + - `check` invalid manifest; + - `check` missing runtime artifact; + - `check` unsafe package archive; + - `pack` deterministic digest; + - `pack` package is discoverable by existing Plugin discovery; + - JSON report shape. +- Validation: focused CLI/plugin authoring tests, relevant `cargo check` / `cargo test`, `cargo fmt --check`, `git diff --check`, and `nix build .#yoi` because product CLI/resources/packaging behavior changes. + +## Non-goals + +- Remote template fetching. +- Publishing or installing from a registry. +- Enabling/disabling Plugins in workspace config. +- Executing Plugin code during `check`. +- crates.io publication of PDK. +- Service / Ingress scaffolding. +- Multi-language templates beyond Rust Component Model Tool. + +## Related work + +- `00001KVG0HR9M` — Objective: Plugin platform roadmap. +- `00001KVHKWNQA` — Rust PDK and embedded authoring templates. +- `00001KVFD3YSV` — Plugin read-only CLI inspection list/show. +- `00001KVG0HR96` — Plugin Component Model runtime. +- `docs/development/plugin-development.md` — current Plugin development guide. diff --git a/.yoi/tickets/00001KVHKWNQS/thread.md b/.yoi/tickets/00001KVHKWNQS/thread.md new file mode 100644 index 00000000..c6d34e33 --- /dev/null +++ b/.yoi/tickets/00001KVHKWNQS/thread.md @@ -0,0 +1,7 @@ + + +## 作成 + +LocalTicketBackend によって作成されました。 + +--- diff --git a/docs/README.md b/docs/README.md index 337e3c9a..5678a0b4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,9 +12,10 @@ It is not a dumping ground for external research, old plans, API inventories, or 4. [`design/profiles-manifests-prompts.md`](design/profiles-manifests-prompts.md) — reusable Profiles, resolved Manifests, and prompt resources. 5. [`design/tool-permissions-scope.md`](design/tool-permissions-scope.md) — tool policy and filesystem scope. 6. [`design/plugin-packages.md`](design/plugin-packages.md) — plugin package distribution, discovery, and enablement boundaries. -7. [`design/memory-knowledge.md`](design/memory-knowledge.md) — generated memory, Knowledge, and audit records. -8. [`development/work-items.md`](development/work-items.md) — how project work is recorded and reviewed. -9. [`development/validation.md`](development/validation.md) — how to check changes. +7. [`development/plugin-development.md`](development/plugin-development.md) — how to build, package, enable, and inspect Yoi Plugins. +8. [`design/memory-knowledge.md`](design/memory-knowledge.md) — generated memory, Knowledge, and audit records. +9. [`development/work-items.md`](development/work-items.md) — how project work is recorded and reviewed. +10. [`development/validation.md`](development/validation.md) — how to check changes. ## What belongs here diff --git a/docs/development/plugin-development.md b/docs/development/plugin-development.md new file mode 100644 index 00000000..0c11bbd7 --- /dev/null +++ b/docs/development/plugin-development.md @@ -0,0 +1,274 @@ +# Plugin development + +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. + +Yoi Plugins are intentionally explicit: + +- putting a package in `.yoi/plugins` only makes it discoverable; +- a Profile/config entry must explicitly enable it; +- Plugin grants must allow its surfaces and host APIs; +- Plugin code runs only through the configured sandbox runtime; +- Tool calls and Tool results use the ordinary Yoi Tool/Worker history path. + +## Current status + +Implemented foundation: + +- package discovery from project/user Plugin stores; +- explicit enablement resolution; +- Tool surface registration; +- Plugin permission grants; +- raw core-Wasm Tool runtime; +- Component Model Tool runtime; +- `https` and `fs` host APIs for Tool runtime; +- read-only `yoi plugin list/show` inspection. + +Still intentionally separate/future work: + +- `yoi plugin new/check/pack` authoring commands; +- polished multi-language SDK/PDK crates; +- Service / Ingress surfaces; +- WebSocket or inbound HTTP for bidirectional bridges; +- public registry/install/update/signature tooling. + +## Package locations + +Yoi discovers `.yoi-plugin` packages from: + +```text +/.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. + +## Package archive format + +A `.yoi-plugin` package is currently a bounded ZIP archive. For now, create it with stored entries, not compressed entries: + +```bash +(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. + +## 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 +``` + +The preferred new runtime is `wasm-component`. The older raw core-Wasm runtime remains explicit for compatibility: + +```toml +[runtime] +kind = "wasm" +entry = "plugin.wasm" +abi = "yoi-plugin-wasm-1" +``` + +Do not rely on package presence to activate anything. Discovery only records inventory. + +## Component Model authoring sketch + +Yoi's Component Model Tool world is stored in `resources/plugin/wit/`. A minimal Rust sketch is available at: + +```text +docs/examples/plugin-component-tool/lib.rs +``` + +The important authoring shape is: + +```rust +wit_bindgen::generate!({ + world: "tool", + path: "../../../resources/plugin/wit", +}); + +struct Plugin; + +impl Guest for Plugin { + fn call(tool_name: String, input_json: String) -> String { + format!( + r#"{{"summary":"component tool {tool_name}","content":"input was {input_json}"}}"# + ) + } +} + +export!(Plugin); +``` + +The returned string is ordinary `ToolOutput` JSON. It is routed through the normal Tool result path; the component cannot inject hidden context. + +The exact build pipeline depends on the authoring toolchain (`wit-bindgen`, component adapter tooling, etc.). Until `yoi plugin new/check/pack` exists, Plugin authors should treat the example as the ABI contract sketch and use `yoi plugin list/show` plus focused runtime tests to verify packages. + +## Enabling a Plugin in a workspace + +Enablement belongs in the resolved Profile/config path for the workspace. For local dogfooding or private experiments, use the ignored local overlay rather than committing secrets or local paths: + +```toml +# .yoi/override.local.toml + +[features] +plugins = true + +[[plugins.enabled]] +id = "project:example.echo" +version = "0.1.0" +digest = "sha256:" +surfaces = ["tool"] + +[plugins.enabled.grants] +id = "project:example.echo" +version = "0.1.0" +digest = "sha256:" +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 +``` + +## `https` host API + +The `https` host API is outbound-only and grant-gated. It is meant for Tool calls such as webhook posting or REST requests. It is not a WebSocket/Gateway or inbound HTTP bridge. + +Manifest permissions should request `host_api.https` in addition to the Tool permissions. Enablement grants must then allow the API and constrain hosts/methods. + +Example grant shape: + +```toml +[plugins.enabled.grants] +permissions = [ + { kind = "surface", surface = "tool" }, + { kind = "tool", name = "discord_post" }, + { kind = "host_api", api = "https" }, +] + +[[plugins.enabled.grants.https]] +host = "discord.com" +methods = ["POST"] +path_prefixes = ["/api/webhooks/"] +``` + +Yoi rejects `http://`, localhost/private/link-local targets, disallowed hosts/methods, oversize requests/responses, and missing grants. Credentials must come from explicit config/secret references, not ambient environment variables. + +## `fs` host API + +The `fs` host API is Plugin-scoped and grant-gated. Plugins do not inherit the Pod/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. + +## Outbound vs bridge integrations + +After `https`, an outbound Discord webhook Tool is feasible: + +```text +Yoi Tool call -> Plugin Tool -> yoi:host/https -> Discord REST/webhook +``` + +A bidirectional Discord bridge is different. It needs a Service surface plus Ingress and either WebSocket/Gateway support or inbound HTTP interactions: + +```text +Discord Gateway/Webhook -> Plugin Service/Ingress -> host routing policy -> notify/run/drop/diagnostic +``` + +Do not model bidirectional bridge work as an `https` Tool alone. + +## 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 `. +6. Add explicit enablement and grants. +7. Re-run `yoi plugin show ` 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 transitional compatibility.