diff --git a/.claude/skills/wavs/SKILL.md b/.claude/skills/wavs/SKILL.md index da41ea1f6..1ba8881d0 100644 --- a/.claude/skills/wavs/SKILL.md +++ b/.claude/skills/wavs/SKILL.md @@ -40,13 +40,18 @@ just setup-claude-mcp [/path/to/project] just start-wavs-dev # 2. Run wavs-mcp (in a separate terminal) -./target/release/wavs-mcp --wavs-url http://localhost:8000 --token +./target/release/wavs-mcp --wavs-url http://localhost:8000 --token \ + --exec-enabled \ + --signing-mnemonic "word1 word2 ... word12" \ + --mcp-chain-credential "0x" # 3. Register with Claude Code npx @wavs/mcp@latest ``` -> **Local tools** (`scaffold_component`, `build_component`, `get_wit_interface`) work without MCP — useful for component development without a running node. +> **Execution tools** (`wavs_exec_*`) require `--exec-enabled`. Tier 2 (`signed_result`) also needs `--signing-mnemonic`. Tier 3 (`on_chain`) also needs `--mcp-chain-credential` and `exec_enabled: true` in the service definition. See [`flows/execution.md`](flows/execution.md). + +> **Local tools** (`scaffold_component`, `build_component`, `validate_component`, `get_wit_interface`, `get_service_schema`) work without MCP — useful for component development without a running node. --- @@ -57,6 +62,7 @@ npx @wavs/mcp@latest | Build a new component from scratch | [`flows/component-dev.md`](flows/component-dev.md) | | Deploy a new service with an on-chain contract | [`flows/deployment.md`](flows/deployment.md) | | Update a deployed service with a new component | [`flows/update-service.md`](flows/update-service.md) | +| Execute a deployed service | [`flows/execution.md`](flows/execution.md) | When in doubt, start with **component-dev** — it ends with a deployment step. @@ -68,9 +74,10 @@ When in doubt, start with **component-dev** — it ends with a deployment step. |----------|-------|---------------| | **Read** | `get_node_info`, `get_health`, `list_services`, `get_service` | None | | **Write** | `deploy_service`, `delete_service` | `--token` | -| **Dev** | `upload_component`, `save_service`, `simulate_trigger`, `deploy_dev_service`, `query_kv` | Dev endpoints enabled | -| **Chain-write** | `set_service_uri`, `deploy_service_manager`, `deploy_poa_service_manager`, `register_operator` | `WAVS_MCP_CHAIN_CREDENTIAL` env var | -| **Local** | `get_wit_interface`, `scaffold_component`, `build_component` | None | +| **Dev** | `upload_component`, `save_service`, `simulate_trigger`, `deploy_dev_service`, `query_kv`, `query_logs`, `query_component_logs` | Dev endpoints enabled | +| **Chain-write** | `set_service_uri`, `deploy_service_manager`, `deploy_poa_service_manager`, `register_operator`, `deploy_and_register` | `WAVS_MCP_CHAIN_CREDENTIAL` env var | +| **Local** | `get_service_schema`, `get_wit_interface`, `scaffold_component`, `build_component`, `validate_component` | None | +| **Execution** | `wavs_exec_*` (dynamic, one per deployed workflow) | `--exec-enabled`; Tier 2 needs `--signing-mnemonic`; Tier 3 needs `--mcp-chain-credential` + `exec_enabled: true` | Full tool reference: [`reference/mcp-tools.md`](reference/mcp-tools.md) @@ -90,12 +97,18 @@ The WAVS app "Register with Claude" button and `just setup-claude-mcp` write thi Dev endpoints must be enabled in `wavs.toml` under `[wavs]`: ```toml -dev_endpoints_enabled = true # Required for upload, save, simulate, deploy_dev +dev_endpoints_enabled = true # Required for upload, save, simulate, deploy_dev, query_logs +``` + +The `exec_enabled` field in a service definition controls Tier 3 (on-chain) execution: +```json +{ "exec_enabled": true } ``` +When omitted or `false`, only Tiers 1–2 are available for that service. See [`reference/service-json.md`](reference/service-json.md). --- ## Reference -- [`reference/mcp-tools.md`](reference/mcp-tools.md) — All 20 tools with auth requirements and parameter notes +- [`reference/mcp-tools.md`](reference/mcp-tools.md) — All 24+ tools with auth requirements and parameter notes (includes dynamic `wavs_exec_*`) - [`reference/service-json.md`](reference/service-json.md) — Service/trigger JSON formats + simulate examples diff --git a/.claude/skills/wavs/flows/component-dev.md b/.claude/skills/wavs/flows/component-dev.md index 0a6225f26..57419dbaf 100644 --- a/.claude/skills/wavs/flows/component-dev.md +++ b/.claude/skills/wavs/flows/component-dev.md @@ -6,13 +6,35 @@ Build, test, and deploy a new WAVS WASM component from scratch. ## Checklist -- [ ] **Step 1** — `wavs:wavs_get_wit_interface` — Read WIT definitions to understand available APIs before writing code. -- [ ] **Step 2** — `wavs:wavs_scaffold_component` — Generate project skeleton (`Cargo.toml` + `src/lib.rs`). -- [ ] **Step 3** — Implement logic in `src/lib.rs` using the patterns below. -- [ ] **Step 4** — `wavs:wavs_build_component` — Compile; read stderr and fix errors; repeat until exit code 0. +- [ ] **Step 1** — `wavs:wavs_scaffold_component` with `dir` parameter — Creates the complete project on disk with all WIT files, bindings, and trigger template. Nothing to write manually. +- [ ] **Step 2** — Customize `src/lib.rs` with your component logic. +- [ ] **Step 3** — `wavs:wavs_build_component` — Compile; read stderr and fix errors; repeat until exit code 0. +- [ ] **Step 4** — `wavs:wavs_validate_component` — Verify the .wasm exports the correct `run` function before uploading. - [ ] **Step 5** — `wavs:wavs_upload_component` — Upload `.wasm`; save the returned digest (raw 64-char hex, no `sha256:` prefix). + - **OCI alternative:** If the component is published to an OCI registry (e.g. ghcr.io), you can skip upload and use an OCI source in the service definition instead: `"source": {"oci": {"uri": "oci://ghcr.io/org/component:v1.0"}}`. See [`reference/service-json.md`](../reference/service-json.md#oci-pull-from-registry-at-deploy-time) for details. - [ ] **Step 6** — `wavs:wavs_deploy_dev_service` (no on-chain contract) **or** follow [`deployment.md`](deployment.md) for a real deployment. -- [ ] **Step 7** — `wavs:wavs_simulate_trigger` — Verify output. +- [ ] **Step 7** — If the `ui_navigate` tool is available (WAVS desktop app embedded agent only), **immediately** call it to open the service detail page (path from deploy output). Don't wait — navigate right after deploy so the user can see the service. +- [ ] **Step 8** — `wavs:wavs_simulate_trigger` — Verify output. + +> **Tip:** Call `wavs:wavs_get_wit_interface` if you need to understand the full WIT API (HTTP, KV, host functions, etc.) before writing custom logic. +> **Tip:** Omit the `dir` parameter from `wavs_scaffold_component` to get file contents as text instead of writing to disk (useful when integrating into existing projects). + +--- + +## How Scaffolding Works + +**With `dir` parameter (recommended):** The tool creates `{dir}/{name}/` with all files ready to build. No manual file creation needed. + +**Without `dir`:** Returns file contents as text. You must write every file yourself, including the `wit/` directory. Use this only when integrating into an existing project. + +The scaffolded project is self-contained: +- Builds with `cargo build --target wasm32-wasip2 --release` (no `cargo-component` needed) +- All WIT interface definitions are bundled in `wit/` +- **Prerequisite:** `rustup target add wasm32-wasip2` + +### In-Workspace Alternative (WAVS repo only) + +If working inside the WAVS monorepo, you can instead create a component at `examples/components/{name}/` using `example-helpers = { workspace = true }`. This is simpler (only 2 files, no WIT copy needed) but only works within the workspace. Build with `cargo component build --release -p {name}`. --- @@ -24,11 +46,9 @@ trigger_type: evm_contract_event | cosmos_contract_event | block_interval | cron description: optional one-line description ``` -Place the generated component at `examples/components/{name}/` to use workspace deps automatically. - --- -## Component Anatomy +## Component Anatomy (In-Workspace) Minimal working component using the prelude: @@ -40,23 +60,24 @@ struct Component; impl Guest for Component { fn run(action: TriggerAction) -> Result, String> { - let (trigger_id, data) = decode_trigger_event(action.data)?; - - // Process `data` bytes and compute output. - let output = data; // echo the raw input for now - - Ok(vec![encode_trigger_output( - trigger_id, - &output, - action.config.service_id, - )]) + match action.data { + TriggerData::Raw(data) => { + // Process raw input bytes + Ok(vec![WasmResponse { + payload: data, + ordering: None, + event_id_salt: None, + }]) + } + _ => Err("Unsupported trigger data type".to_string()), + } } } export_layer_trigger_world!(Component); ``` -The prelude re-exports: `Guest`, `TriggerAction`, `WasmResponse`, `decode_trigger_event`, `encode_trigger_output`, `export_layer_trigger_world`, and the `host` module. +The prelude re-exports: `Guest`, `TriggerAction`, `TriggerData`, `Trigger`, `WasmResponse`, `decode_trigger_event`, `encode_trigger_output`, `export_layer_trigger_world`, and the `host` module. Full explicit imports (when you need specific types): @@ -73,11 +94,60 @@ use example_helpers::export_layer_trigger_world; use example_helpers::trigger::{decode_trigger_event, encode_trigger_output}; ``` +## Component Anatomy (Standalone) + +```rust +#[allow(warnings)] +mod bindings; + +use crate::bindings::{ + export, + wavs::types::events::TriggerData, + Guest, TriggerAction, WasmResponse, +}; + +struct Component; +export!(Component with_types_in bindings); + +impl Guest for Component { + fn run(action: TriggerAction) -> std::result::Result, String> { + match action.data { + TriggerData::Raw(data) => { + let payload = data; // echo input back + Ok(vec![WasmResponse { + payload, + ordering: None, + event_id_salt: None, + }]) + } + _ => Err("Unsupported trigger data type".to_string()), + } + } +} +``` + +With `src/bindings.rs`: +```rust +#[allow(warnings)] +mod _inner { + wit_bindgen::generate!({ + world: "wavs-world", + path: "wit", + pub_export_macro: true, + generate_all, + features: ["tls"], + }); +} +pub use _inner::*; +``` + --- ## Host APIs ```rust +// In-workspace: available via `host::` directly +// Standalone: use `crate::bindings::host` host::config_var("my-key") // → Option; reads service config host::log(host::LogLevel::Info, "msg"); // levels: Debug, Info, Warn, Error host::get_service() // → ServiceInfo (manager address, config, etc.) @@ -91,18 +161,26 @@ host::get_event_id(Some(salt)) // custom salt (Vec) ```rust match action.data { - TriggerData::EvmContractEvent(_) | TriggerData::CosmosContractEvent(_) => { - let (trigger_id, data) = decode_trigger_event(action.data)?; + TriggerData::EvmContractEvent(event_data) => { + // event_data.chain: chain key string + // event_data.log.data.data: raw ABI-encoded log bytes + // event_data.log.data.topics: Vec of topic byte arrays + } + TriggerData::CosmosContractEvent(event_data) => { + // event_data.chain: chain key string + // event_data.event.ty: event type string + // event_data.event.attributes: Vec<(String, String)> + // event_data.block_height: u64 } TriggerData::Raw(bytes) => { - // Plain bytes — trigger_id is 0 for raw/manual triggers. - let data = bytes; + // Plain bytes — for manual/raw triggers } - TriggerData::Cron { trigger_time } => { - // trigger_time: unix timestamp (u64) + TriggerData::Cron(data) => { + // data.trigger_time.nanos: unix timestamp in nanoseconds } - TriggerData::BlockInterval { block_height } => { - // block_height: u64 + TriggerData::BlockInterval(data) => { + // data.block_height: u64 + // data.chain: chain key string } _ => return Err("unsupported trigger type".to_string()), } @@ -116,6 +194,7 @@ match action.data { ```rust use example_helpers::bindings::world::wasi::keyvalue::{store, atomics}; +// Standalone: use crate::bindings::wasi::keyvalue::{store, atomics}; let bucket = store::open("my-bucket").map_err(|e| e.to_string())?; let value: Option> = bucket.get("key").map_err(|e| e.to_string())?; @@ -139,7 +218,7 @@ let bytes = block_on(async { --- -## Cargo.toml Template +## Cargo.toml Template (In-Workspace) Place at `examples/components/{name}/Cargo.toml`: @@ -147,18 +226,22 @@ Place at `examples/components/{name}/Cargo.toml`: [package] name = "{name}" edition.workspace = true +version.workspace = true +authors.workspace = true +rust-version.workspace = true +repository.workspace = true [lib] crate-type = ["cdylib"] [package.metadata.component] -package = "wavs-user:{name}" +package = "component:{name}" [dependencies] -example-helpers = { path = "../../_helpers" } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -# Add for KV store or outbound HTTP: +example-helpers = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +# Uncomment for async HTTP, timers, etc: # wstd = { workspace = true } ``` @@ -166,11 +249,16 @@ serde_json = "1" ## Build Output Paths -After `wavs_build_component` (release mode): +After `wavs_build_component` or `cargo component build --release` (workspace): ``` target/wasm32-wasip1/release/{package_name_with_underscores}.wasm ``` +After `cargo build --target wasm32-wasip2 --release` (standalone): +``` +target/wasm32-wasip2/release/{package_name_with_underscores}.wasm +``` + After `just wasi-build-native {component-name}` (from repo root): ``` examples/build/components/{component-name}.wasm @@ -183,13 +271,17 @@ Use the **absolute path** when calling `wavs_upload_component`. ## Debugging **Build errors** (read `stderr` from `wavs_build_component`): -- `cannot find type` / `unresolved import` — check `example-helpers` path; try `use example_helpers::prelude::*` +- `cannot find type` / `unresolved import` — check import paths match your scaffold mode (workspace vs standalone) +- `failed to create a target world` / `package not found` — WIT files are missing. For standalone: ensure all `wit/deps/*/package.wit` files are written. For workspace: ensure `example-helpers` path is correct. - `the trait bound is not satisfied` — `encode_trigger_output` needs `&[u8]` or `AsRef<[u8]>` -- `does not implement Guest` — ensure `export_layer_trigger_world!(Component)` is present +- `does not implement Guest` — ensure export macro is present (`export_layer_trigger_world!` for workspace, `export!` for standalone) +- `no export 'run' found` — the export macro is missing or the `Guest` impl is not correct **Runtime errors** (from `wavs_simulate_trigger`): - Error message comes directly from your `?` or `return Err(...)` calls - Add `host::log(host::LogLevel::Debug, &format!("data: {:?}", data))` and re-simulate +- Use `wavs_query_component_logs(service_id="", level="debug")` to read component `host::log()` output after simulation +- Use `wavs_query_logs(target="wavs::subsystems::engine", level="warn")` for broader engine-level diagnostics **Missing config vars** — component returns `"config var X not found"`: - Service definition must include the key in its `config` map diff --git a/.claude/skills/wavs/flows/deployment.md b/.claude/skills/wavs/flows/deployment.md index 04ab2bae5..2c5990c03 100644 --- a/.claude/skills/wavs/flows/deployment.md +++ b/.claude/skills/wavs/flows/deployment.md @@ -18,13 +18,16 @@ Deploy a new WAVS service with an on-chain ServiceManager contract. - **SimpleServiceManager** (lightweight PoA): `wavs:wavs_deploy_service_manager` — returns `address` - **POAStakeRegistry** (full middleware): `wavs:wavs_deploy_poa_service_manager` — returns proxy `address`; requires Docker - [ ] **Step 3** — `wavs:wavs_upload_component` — Upload the compiled `.wasm`; save the returned digest (raw 64-char hex, no `sha256:` prefix). + - **OCI alternative:** If the component is published to an OCI registry, skip upload and use `"source": {"oci": {"uri": "oci://ghcr.io/org/component:v1.0"}}` in the service definition. The WAVS node pulls it at deploy time. See [`reference/service-json.md`](../reference/service-json.md#oci-pull-from-registry-at-deploy-time). - [ ] **Step 4** — `wavs:wavs_save_service` — Save the service definition JSON; get back a URI. - [ ] **Step 5** — `wavs:wavs_set_service_uri` — Call `setServiceURI` on-chain with the URI from step 4. - [ ] **Step 6** — `wavs:wavs_deploy_service` — Register the service with the WAVS node (reads definition from chain). - **Verify the node picked up the service:** Call `wavs:wavs_get_service(chain, address)` and confirm `status: active`. Note: `wavs_deploy_service` returning success only means the node fetched and stored the config from the on-chain URI. Confirm active status with `wavs_get_service`. + - If the `ui_navigate` tool is available (WAVS desktop app embedded agent only), **immediately** call it to open the service detail page (use the path from the deploy output). Don't wait — navigate right after deploy so the user can see the service. - [ ] **Step 7 (POA only)** — `wavs:wavs_register_operator` — Register the node's signing key on the POAStakeRegistry. **Must be called AFTER `wavs_deploy_service`** so the node has assigned a service-specific HD-derived signing key to register on-chain. - [ ] **Step 8** — `wavs:wavs_simulate_trigger` — Smoke test. - [ ] **Step 9** — `wavs:wavs_list_services` — Confirm the service appears with `status: active`. +- [ ] **Step 10 (optional)** — If `--exec-enabled` is set on the MCP server, the service is now available as a `wavs_exec_*` tool. Follow [`flows/execution.md`](execution.md) to execute it at any trust tier. --- @@ -86,6 +89,7 @@ Pass to `wavs_save_service` or `wavs_deploy_dev_service`: { "name": "my-service", "status": "active", + "exec_enabled": true, "manager": { "evm": { "chain": "evm:31337", diff --git a/.claude/skills/wavs/flows/execution.md b/.claude/skills/wavs/flows/execution.md new file mode 100644 index 000000000..e06c4aecf --- /dev/null +++ b/.claude/skills/wavs/flows/execution.md @@ -0,0 +1,153 @@ +# Execution Flow + +Execute a deployed WAVS service workflow directly through MCP tools. + +--- + +## Prerequisites + +1. **Service is deployed** — the service must appear in `wavs_list_services` output +2. **MCP server started with `--exec-enabled`** — without this flag, `wavs_exec_*` tools do not appear +3. **Tier-specific config** (see [Trust Tier Selection](#trust-tier-selection) below) + +--- + +## Checklist + +- [ ] **Step 1** — `wavs:wavs_list_services` — Find the deployed service and note its name + workflow IDs. +- [ ] **Step 2** — Identify the execution tool: `wavs_exec_{service_name}_{workflow_id}` (service name is lowercased, non-alphanumeric chars become `_`, max 64 chars). +- [ ] **Step 3** — Choose a trust tier based on your needs (see table below). +- [ ] **Step 4** — Call the execution tool with `trust_tier`, `input`, and optional `timeout_ms`. +- [ ] **Step 5 (Tier 3 only)** — Receive gas estimate + nonce, then call again with `confirm: ""` within 60 seconds. + +--- + +## Trust Tier Selection + +| Tier | `trust_tier` value | What you get | MCP server requirements | When to use | +|------|-------------------|--------------|------------------------|-------------| +| **1** | `result_only` | Raw component output (text or hex) | `--exec-enabled` | Quick testing, data queries, no trust guarantees needed | +| **2** | `signed_result` | Component output + operator signature | `--exec-enabled` + `--signing-mnemonic` | Verifiable off-chain results, attestations | +| **3** | `on_chain` | Transaction hash (result submitted to chain) | `--exec-enabled` + `--signing-mnemonic` + `--mcp-chain-credential` + service `exec_enabled: true` | On-chain settlement, triggering contract state changes | + +--- + +## Examples + +### Tier 1 — result_only + +``` +wavs_exec_echo_data_default( + trust_tier="result_only", + input={"message": "Hello WAVS"} +) +→ Hello WAVS +``` + +The raw component output is returned. If the payload is valid UTF-8, it is shown as text; otherwise as `0x`-prefixed hex. + +### Tier 2 — signed_result + +``` +wavs_exec_echo_data_default( + trust_tier="signed_result", + input={"message": "Hello WAVS"} +) +→ { + "payload": "0x48656c6c6f2057415653", + "signature": "0xabc123...", + "signer": "0xdef456..." + } +``` + +The component output is wrapped with the operator's cryptographic signature. The signer address corresponds to the service's HD-derived signing key (viewable via `wavs_get_service_signer`). + +**Requires:** `--signing-mnemonic` configured on the MCP server (same mnemonic the WAVS node uses). + +### Tier 3 — on_chain (two-step) + +**Step 1: Estimate gas** +``` +wavs_exec_my_service_default( + trust_tier="on_chain", + input={"data": "some payload"} +) +→ { + "status": "pending_confirmation", + "nonce": "0018a3f5b2c1d4e6", + "gas_estimate": "210000", + "chain": "evm:31337", + "message": "Confirm within 60 seconds by passing confirm=\"0018a3f5b2c1d4e6\"" + } +``` + +**Step 2: Confirm submission** (must call within 60 seconds) +``` +wavs_exec_my_service_default( + trust_tier="on_chain", + confirm="0018a3f5b2c1d4e6" +) +→ { + "status": "submitted", + "tx_hash": "0x789abc..." + } +``` + +**Requires:** +- `--signing-mnemonic` and `--mcp-chain-credential` on the MCP server +- `exec_enabled: true` in the service definition (see [`reference/service-json.md`](../reference/service-json.md)) +- The nonce expires after 60 seconds — if missed, re-execute from Step 1 + +--- + +## Error Codes + +| Error Code | Meaning | Common Cause | +|------------|---------|-------------| +| `EXECUTION_TIMEOUT` | Component did not complete within `timeout_ms` | Increase `timeout_ms` (max 25000), or the component is hanging | +| `TIER_NOT_ENABLED` | Requested tier is not available | Missing `--signing-mnemonic` (Tier 2), `--mcp-chain-credential` (Tier 3), or `exec_enabled: true` (Tier 3) | +| `SERVICE_NOT_FOUND` | Tool name does not match any deployed service | Service may have been deleted; call `wavs_list_services` to check | +| `COMPONENT_FAILED` | WASM component returned an error or no output | Check component logic; use `wavs_query_component_logs` to see `host::log()` output | +| `SIGNING_FAILED` | Operator signature could not be produced (Tier 2) | Verify `--signing-mnemonic` matches the WAVS node's mnemonic | +| `SUBMISSION_FAILED` | On-chain transaction reverted or failed (Tier 3) | Check gas, chain RPC health, contract state; the error may include a `partial_result` with the raw component output | + +Errors include a `partial_result` field when the component succeeded but a later step (signing/submission) failed. The partial result contains the raw hex-encoded component output so it is not lost. + +--- + +## Debugging + +### Check component logs + +Use `wavs_query_component_logs` to see what the component printed via `host::log()`: + +``` +wavs_query_component_logs( + service_id="<64-char hex from wavs_list_services>", + level="debug" +) +→ { "entries": [...], "next_id": 42 } +``` + +Filter further with `workflow_id` or `digest` parameters. + +### Check node-level logs + +Use `wavs_query_logs` for broader system logs: + +``` +wavs_query_logs( + target="wavs::subsystems::engine", + level="warn" +) +``` + +### Common issues + +| Symptom | Investigation | +|---------|--------------| +| Tool not in `list_tools` | Verify `--exec-enabled` and that the service is deployed | +| `SERVICE_NOT_FOUND` after deploy | Service cache has a 5-second TTL — wait and retry, or call `wavs_list_services` first | +| `TIER_NOT_ENABLED` for Tier 3 | Check both `--mcp-chain-credential` and `exec_enabled: true` in service definition | +| Confirmation expired | Nonces expire after 60 seconds — re-execute the tool to get a fresh estimate | +| Garbled output | Component may be returning binary data — check if it should produce hex or UTF-8 | diff --git a/.claude/skills/wavs/reference/mcp-tools.md b/.claude/skills/wavs/reference/mcp-tools.md index 1d00d5d37..18fa52422 100644 --- a/.claude/skills/wavs/reference/mcp-tools.md +++ b/.claude/skills/wavs/reference/mcp-tools.md @@ -26,9 +26,14 @@ All tools are exposed by the `wavs` MCP server. Prefix with `wavs:` when calling | `wavs_simulate_trigger` | — | — | ✓ | Fires a test trigger against a deployed service | | `wavs_deploy_dev_service` | — | — | ✓ | Registers service directly without on-chain contract | | `wavs_query_kv` | — | — | ✓ | Reads a value from a service's KV store | +| `wavs_query_logs` | — | — | ✓ | Query structured log entries from WAVS node ring buffer | +| `wavs_query_component_logs` | — | — | ✓ | Query WASM component execution logs (filterable by service_id, workflow_id, digest) | +| `wavs_get_service_schema` | — | — | — | Returns minimal valid Service JSON examples for every trigger type (local) | | `wavs_get_wit_interface` | — | — | — | Returns full WIT interface definitions (local, no network) | | `wavs_scaffold_component` | — | — | — | Generates Cargo.toml + src/lib.rs skeleton (local) | -| `wavs_build_component` | — | — | — | Runs `cargo component build`; returns build output (local) | +| `wavs_build_component` | — | — | — | Builds component; auto-detects standalone (`wasm32-wasip2`) vs workspace (`cargo component`) mode (local) | +| `wavs_validate_component` | — | — | — | Validates .wasm exports correct `run` function; checks WAVS interface compatibility (local, requires `wasm-tools`) | +| `wavs_exec_*` | — | Tier 2–3 | ✓ | Dynamic, one per deployed service workflow. Requires `--exec-enabled`. Auth depends on trust tier. | **Legend:** - Token: MCP server must be started with `--token `; pass token in requests @@ -130,13 +135,57 @@ key: key within the bucket dir: directory containing the component's Cargo.toml release: optional bool (default: true) ``` +Auto-detects build mode: standalone projects (with local `wit/` dir) use `cargo build --target wasm32-wasip2`, workspace projects use `cargo component build`. + +### wavs_validate_component +``` +wasm_path: path to compiled .wasm file +``` +Validates the component is a proper WASI component with the correct `run` export. Run after build, before upload. Requires `wasm-tools` installed. ### wavs_scaffold_component ``` name: lowercase-with-hyphens component name trigger_type: evm_contract_event | cosmos_contract_event | block_interval | cron | manual +dir: optional — parent directory to create project in (e.g. "/tmp"). If provided, writes all files to {dir}/{name}/. + If omitted, returns file contents as text. description: optional string ``` +When `dir` is provided, creates a complete self-contained project with Cargo.toml, src/lib.rs, src/bindings.rs, and the full wit/ directory. Builds with `cargo build --target wasm32-wasip2 --release`. + +### wavs_query_logs +``` +since_id: optional u64 — return entries with id >= this value; pass `next_id` from previous response to page forward (default: 0) +limit: optional usize — max entries to return (default: 100, max: 1000) +level: optional string — minimum log level: trace | debug | info | warn | error (returns this level and above) +target: optional string — filter by target prefix, e.g. "wavs" or "wavs::subsystems::engine" +``` +Returns: `{ "entries": [...], "next_id": }`. Pass `next_id` as `since_id` on the next call to page forward. + +### wavs_query_component_logs +``` +since_id: optional u64 — page forward from this ID (default: 0) +limit: optional usize — max entries (default: 100, max: 1000) +level: optional string — minimum log level: trace | debug | info | warn | error +service_id: optional string — filter to a specific service (64-char hex) +workflow_id: optional string — filter to a specific workflow, e.g. "default" +digest: optional string — filter to a specific component digest (sha256 hex) +``` +Returns same shape as `wavs_query_logs`. Automatically scoped to `wavs::subsystems::engine::wasm_engine` logs. Entries contain component `host::log()` output plus service_id, workflow_id, and digest in the `fields` string. + +### wavs_get_service_schema +No parameters. Returns minimal valid Service JSON examples for every trigger type (manual, cron, block_interval, evm_contract_event, cosmos_contract_event), submit options, and `data_json` formats for `wavs_simulate_trigger`. + +### wavs_exec_* (dynamic execution tools) +One tool is generated per deployed service workflow, named `wavs_exec_{service_name}_{workflow_id}`. These tools only appear when the MCP server is started with `--exec-enabled`. + +``` +input: optional object — data to pass to the component (structure depends on component's WIT interface) +trust_tier: required string — "result_only" | "signed_result" | "on_chain" +timeout_ms: optional integer — per-call timeout in ms (default: 25000, max: 25000) +confirm: optional string — for on_chain tier: pass the nonce from the gas estimate to confirm submission +``` +See [`flows/execution.md`](../flows/execution.md) for the full execution lifecycle and trust tier guide. --- @@ -148,6 +197,9 @@ The MCP server binary is `wavs-mcp`. Key CLI args: |-----|-------------| | `--wavs-url ` | WAVS node HTTP API URL (e.g. `http://localhost:8000`) | | `--token ` | Auth token (enables write tools) | +| `--exec-enabled` | Enable dynamic `wavs_exec_*` execution tools for deployed services | +| `--signing-mnemonic ` | Operator signing mnemonic (required for Tier 2 `signed_result`). Falls back to `WAVS_SIGNING_MNEMONIC` env var or `signing_mnemonic` in `~/.wavs/wavs.toml`. | +| `--mcp-chain-credential ` | Chain credential private key (required for Tier 3 `on_chain`). Falls back to `WAVS_MCP_CHAIN_CREDENTIAL` env var or `mcp_chain_credential` in `~/.wavs/wavs.toml`. | The WAVS node URL and token can also be found by inspecting the running `wavs-mcp` process: ```bash @@ -159,3 +211,4 @@ Environment variables: - `WAVS_TOKEN` — auth token - `WAVS_MCP_CHAIN_CREDENTIAL` — credential for on-chain ops (falls back to `mcp_chain_credential` in `~/.wavs/wavs.toml`) - `WAVS_SIGNING_MNEMONIC` — signing mnemonic (falls back to `signing_mnemonic` in `~/.wavs/wavs.toml`) +- `WAVS_EXEC_ENABLED` — set to `true` to enable execution tools (equivalent to `--exec-enabled`) diff --git a/.claude/skills/wavs/reference/service-json.md b/.claude/skills/wavs/reference/service-json.md index aaf44bcf1..1e09109f2 100644 --- a/.claude/skills/wavs/reference/service-json.md +++ b/.claude/skills/wavs/reference/service-json.md @@ -70,8 +70,9 @@ Used by: `wavs_save_service`, `wavs_deploy_dev_service` - `status`: `"active"` or `"paused"` - `manager`: ServiceManager contract (EVM or Cosmos) - `workflows`: map of `workflow_id` → workflow definition; `workflow_id` is lowercase alphanumeric 3–36 chars -- `component.source.digest`: raw 64-char hex string returned by `wavs_upload_component` (no `sha256:` prefix) +- `component.source`: see [Component Source Types](#component-source-types) below - `submit`: `"none"` to discard results, or `{"aggregator": {...}}` for on-chain submission +- `exec_enabled`: optional bool — when `true`, enables Tier 3 (`on_chain`) execution via `wavs_exec_*` tools. Omit or set to `false`/`null` to disable. Only relevant when using execution tools with `--exec-enabled`. Multiple workflows in one service: ```json @@ -85,6 +86,51 @@ Multiple workflows in one service: --- +## Component Source Types + +The `component.source` field specifies where the WASM binary lives. Three variants are supported: + +### Digest (uploaded component) +```json +"source": { + "digest": "f0b42a5171c9dcd75eac41c8ce2c4e7882d304c885266d8ac7b70af996b9a420" +} +``` +Raw 64-char hex string returned by `wavs_upload_component` (no `sha256:` prefix). The component must already be uploaded to the node. + +### OCI (pull from registry at deploy time) +```json +"source": { + "oci": { + "uri": "oci://ghcr.io/layerlabs/echo-data:v1.0", + "digest": "f0b42a5171c9dcd75eac41c8ce2c4e7882d304c885266d8ac7b70af996b9a420" + } +} +``` +The WAVS node pulls the component from an OCI-compliant registry (ghcr.io, Docker Hub, etc.) when the service is deployed. The `digest` field is optional — when provided, the pulled bytes are verified against it. When omitted, the component is pulled by tag only (a warning is emitted). + +**URI formats:** +- `oci://ghcr.io/org/component:tag` — pull by tag (mutable, may change) +- `oci://ghcr.io/org/component@sha256:abc123...` — pin to manifest digest +- `oci://ghcr.io/org/component:tag@sha256:abc123...` — tag + manifest pin + +**Auth:** For private registries, set `WAVS_OCI_USERNAME` and `WAVS_OCI_PASSWORD` env vars on the WAVS node. Both must be set for Basic auth; otherwise falls back to anonymous. + +**No upload needed:** When using OCI source, skip the `wavs_upload_component` step — the node pulls directly from the registry. + +### Download (fixed URL) +```json +"source": { + "download": { + "uri": "https://example.com/my-component.wasm", + "digest": "f0b42a5171c9dcd75eac41c8ce2c4e7882d304c885266d8ac7b70af996b9a420" + } +} +``` +Downloads the component from a fixed URL at deploy time. The `digest` is required and verified after download. + +--- + ## Trigger Types ### Cron diff --git a/.gitignore b/.gitignore index e91be62d1..45e800e3c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,7 @@ __pycache__ # generated by prepack packages/wavs-mcp/skill/ + +# node data +.wavs-data +.wavs-data-cli diff --git a/.planning/MILESTONES.md b/.planning/MILESTONES.md new file mode 100644 index 000000000..ddf029d22 --- /dev/null +++ b/.planning/MILESTONES.md @@ -0,0 +1,86 @@ +# Milestones + +## v3.0 Agent Composition (Shipped: 2026-04-23) + +**Phases completed:** 4 phases, 8 plans, 8 tasks + +**Key accomplishments:** + +- One-liner: +- AllowedServiceCalls enum +- 1. [Rule 1 - Bug] Fixed pre-existing compile errors from previous phases +- 1. [Rule 1 - Bug] execute_legacy() used WavsWorld requiring agent export — broke all legacy components +- `packages/wavs/src/subsystems/engine/rpc_caller.rs` +- Component (`examples/components/multi-step-agent/src/lib.rs`): +- utility-service + +--- + +## v2.0 Agent Runtime (Shipped: 2026-04-20) + +**Phases completed:** 3 phases, 7 plans, 1 tasks + +**Key accomplishments:** + +- One-liner: +- One-liner: +- One-liner: +- One-liner: +- One-liner: +- One-liner: +- service.json with AllowedHostPermission::Only(["api.anthropic.com"]) and env_keys wired to WAVS_ENV_ANTHROPIC_API_KEY; agent_example.wasm built at 1.3MB via cargo-component + +--- + +## v1.3 Activity UX & Bug Fixes (Shipped: 2026-04-09) + +**Phases completed:** 4 phases, 4 plans, 2 tasks + +**Key accomplishments:** + +- One-liner: +- 1. [Rule 1 - Bug] Fixed pre-existing missing exec_enabled field in block_interval test + +--- + +## v1.2 Components Explorer (Shipped: 2026-04-08) + +**Phases completed:** 3 phases, 4 plans, 4 tasks + +**Key accomplishments:** + +- TypeScript types, Tauri command wrappers, useComponentDetail hook, and ComponentDetailPage shell with breadcrumb, header card, and tab navigation at /components/:digest +- One-liner: + +--- + +## v1.1 Open Source AI Providers & Settings UX (Shipped: 2026-04-08) + +**Phases completed:** 3 phases, 3 plans, 6 tasks + +**Key accomplishments:** + +- Groq and OpenRouter added as selectable agent providers with dynamic model placeholders and settings-aware sidecar startup via settings.json read at startup. +- Ollama added as selectable agent provider with conditional base URL field, models.json generation from Rust backend, and ModelRegistry.create() sidecar switch for OpenAI-compatible local model support + +--- + +## v1.0 WAVS Improvements (Shipped: 2026-04-07) + +**Phases completed:** 6 phases, 12 plans, 23 tasks + +**Key accomplishments:** + +- ComponentSource::Oci variant with optional digest and OCI puller module using oci-client 0.16 / oci-wasm 0.4 for authenticated WASM component pulls +- OCI pull wired into engine pipeline with tuple return, digest verification, cache-hit optimization, unpinned-tag warning, and all 10 call sites updated for Option<&ComponentDigest> +- WIT-to-JSON Schema conversion library with recursive type mapping, $defs deduplication, digest-based caching, and WIT doc comment enrichment +- Execution types, error codes, schema merging, service cache, ExecContext, --exec-enabled flag, and POST /dev/execute endpoint for synchronous component result retrieval +- End-to-end MCP execution pipeline: dynamic tool discovery from deployed services via list_tools(), Tier 1 result_only dispatch via call_tool() with timeout enforcement, and peer-based list_changed notifications on service CRUD +- Three trust tiers complete: signed_result returns operator EIP-191 signature with HD-derived key; on_chain implements two-step estimate/submit flow via EvmSigningClient with real tx_hash +- 1. [Rule 2 - Missing null checks] Updated ActivityCard.tsx and ActivityFeed.tsx for optional triggerData +- 1221-line monolithic Settings.tsx decomposed into 4 isolated section components + SettingsSidebar + 615-line orchestrating shell with sidebar navigation and parent OAuth listener +- One-liner: +- useGroupedActivity hook with single-pass correlationId grouping and appStore ERR-02 eviction guard preserving failed events from FIFO removal +- GroupedActivityCard component and ActivityFeed refactor delivering nested trigger-submission cards with amber/red status dots, full error display, and status-based filter tabs replacing kind-based tabs + +--- diff --git a/.planning/PROJECT.md b/.planning/PROJECT.md new file mode 100644 index 000000000..09e03b358 --- /dev/null +++ b/.planning/PROJECT.md @@ -0,0 +1,137 @@ +# WAVS Agent Runtime + +## What This Is + +WAVS is a platform for running sandboxed WebAssembly services with cryptographic trust guarantees. v1.0–v1.3 shipped developer experience improvements: OCI distribution, WIT-to-schema, MCP execution with three trust tiers, open-source AI providers, component explorer, activity feed UX, and service reliability fixes. v2.0 makes WAVS a first-class agent runtime — developers write rig-based agents in ~30 lines of Rust that autonomously reason and act inside the WASM sandbox. + +## Current Milestone: v3.0 Agent Composition + +**Goal:** Agents can reason across multiple invocations and call other deployed services, enabling multi-step autonomous workflows and composable service architectures. + +**Target features:** +- Agent continuation mode — `Continue`/`Done` WIT return variants with auto-persisted state +- Service-to-service synchronous RPC via `call-service` host function +- Permission-based service calling (`AllowedServiceCalls` in service.json) +- Both agent-decided and developer-defined multi-step workflows +- Auto-persist conversation + tool results to KV between steps (with override) + +## Core Value + +Developers can write an autonomous LLM agent in ~30 lines of Rust, compile it to WASM, deploy it as a WAVS service, and have it reason + act on triggers with the same sandbox and cryptographic trust guarantees as any other WAVS component. + +## Requirements + +### Validated + +- ✓ Sandboxed WASM component execution via Wasmtime — existing +- ✓ Per-component network policy (`All` / `Only` / `None` on `AllowedHostPermission`) — existing +- ✓ Cryptographic result signatures by operators — existing +- ✓ Multi-operator execution with configurable quorum — existing +- ✓ EVM and Cosmos blockchain read/write — existing +- ✓ Event-driven execution (EVM logs, Cosmos events, HTTP webhooks, cron) — existing +- ✓ MCP server for service management (deploy, upload, register, simulate) — existing (`wavs-mcp`) +- ✓ Tauri 2 desktop app with wallet, health, service management, logging — existing +- ✓ Self-governing service configuration via on-chain actors — existing +- ✓ OCI component pull — `oci://` URIs in service.json, digest-verified pull and caching — v1.0 +- ✓ WIT-to-schema tooling — JSON Schema from component WIT interfaces — v1.0 +- ✓ End-user MCP execution interface — deployed components as callable MCP tools — v1.0 +- ✓ Three trust tiers per tool call: result only / signed result / on-chain submission — v1.0 +- ✓ Event correlation IDs — trigger/submission events linked by correlationId — v1.0 +- ✓ Submission failure surfacing — SubmissionFailed events reach GUI with error messages — v1.0 +- ✓ Settings page decomposition — sidebar-navigated layout with isolated section components — v1.0 +- ✓ Unified activity frontend — nested parent-child events with status filtering and error display — v1.0 +- ✓ Groq & OpenRouter agent providers — selectable from settings dropdown with API key persistence — v1.1 +- ✓ Ollama agent provider — custom base URL, models.json generation, ModelRegistry.create() for local models — v1.1 +- ✓ Settings scroll refactor — single scrollable page with IntersectionObserver sidebar tracking — v1.1 +- ✓ Tauri commands exposing wit-schema JSON Schema and component metadata — v1.2 +- ✓ Component detail page with full interface profile (functions, permissions, config) — v1.2 +- ✓ Improved components list with search/filter, richer cards, and detail navigation — v1.2 +- ✓ Richer activity cards with trigger, result summary, and submission info visible without expanding — v1.3 +- ✓ Smart result decoding for activity feed (UTF-8 → JSON → hex) — v1.3 +- ✓ Service restart reliability fix — v1.3 +- ✓ Wallet settings kebab dropdown for uncommon actions — v1.3 + +- ✓ WASI-compatible rig fork (reqwest/tokio optional, cfg unified) — v2.0 +- ✓ `wavs-rig` integration crate bridging rig into WASI sandbox — v2.0 +- ✓ WAVS host functions exposed as typed rig tools (5 tools) — v2.0 +- ✓ KV-backed conversation memory with token budget truncation — v2.0 +- ✓ Example agent component with full LLM reasoning loop — v2.0 +- ✓ Agent deployment with AllowedHostPermission::Only sandbox — v2.0 (partial — engine enforcement pending) + +### Active + + + +- [ ] Agent continuation mode — Continue/Done return variants in WIT +- [ ] Auto-persist agent state (conversation, tool results) between continuation steps +- [ ] Developer-defined multi-step workflows with explicit handoffs +- [ ] Service-to-service synchronous RPC via call-service host function +- [ ] AllowedServiceCalls permission in service.json (caller declares callable targets) +- [ ] Engine re-invocation loop for continuation mode +- [ ] Engine inter-service dispatch for call-service + +### Out of Scope + +- Demo/doc the `Only` allowlist variant — tracked separately, different repo +- OCI component publishing tooling — deferred to future phase (pull-only shipped) +- Wassette feature parity comparison docs — marketing concern, not code +- MCP stdio transport signing — Stdio is local-process; trust boundary is machine-level + +## Context + +**Current State:** v2.0 shipped. Agent runtime foundation complete (rig-wasi fork, wavs-rig integration, example agent). Now building composition layer (continuation + RPC). + +**Tech stack:** Rust (node, CLI, MCP server, types), Tauri 2 + React 19 + Vite 7 (desktop app), Wasmtime (WASI component execution), Zustand (frontend state). New for v2.0: rig-core (Rust agent framework), wasm32-wasip2 target. + +**Key context for v2.0:** +- rig-core has WASM-compatible traits (`WasmCompatSend`/`WasmCompatSync`, `HttpClientExt`) but hard blockers: unconditional reqwest, tokio rt feature, cfg inconsistencies. ~300-500 line fork needed. +- WAVS already has `wasi:http/outgoing-handler` and `wasi:keyvalue` host functions — these become the rig bridges. +- `AllowedHostPermission` (`All`/`Only`/`None`) enforces network policy on LLM API calls at the Wasmtime level. +- Sequential tool execution is fine for MVP (WASI is single-threaded). Configure rig concurrency to 1. +- Existing components use `wstd::runtime::block_on` for async — rig's agent loop needs to work within this. + +## Key Decisions + +| Decision | Rationale | Outcome | +|----------|-----------|---------| +| Extend wavs-mcp rather than separate server | Single MCP server for both management and execution reduces user friction | ✓ Good — clean integration | +| OCI pull-only for v1 (no publish tooling) | Lower scope; publishing adds complexity without immediate user value | ✓ Good | +| Three trust tiers as explicit agent choice | Matches the "dial not binary" positioning; agents pick what they need | ✓ Good | +| WIT-to-schema before MCP execution | Auto-generated tool descriptions are core to the Wassette-parity experience | ✓ Good | +| correlation_id as String not Uuid type | Avoids bincode derive complications; String implements bincode natively | ✓ Good | +| OAuth listener in parent Settings.tsx | Survives section navigation since parent never unmounts | ✓ Good | +| Client-side correlationId grouping | Simple, no backend changes needed; single-pass useMemo in ActivityFeed | ✓ Good | +| Status-based filter tabs (All/Pending/Failed/Complete) | More useful than kind-based (trigger/submission) now that events are nested | ✓ Good | +| Groq/OpenRouter as KnownProviders | Already in pi-ai — zero Rust changes, just UI + settings.json read at startup | ✓ Good | +| models.json for Ollama (not registerProvider) | Declarative, auto-reloads on /model calls, no extension code | ✓ Good | +| IntersectionObserver for scroll tracking | Native, performant, no scroll event spam — sidebar highlight stays in sync | ✓ Good | +| result_payload as Option pre-encoded hex | serde_helpers module is private; pre-encode in aggregator avoids cross-crate dep | ✓ Good | +| 4KB cap on result_payload at aggregator | Prevents 100MB hex blowup in Tauri IPC; enforced before channel send | ✓ Good | +| Pending subscription queue for EVM triggers | Standard async ordering fix; queues commands before controller ready, drains after | ✓ Good | +| Kebab menu for uncommon wallet actions | Reduces vertical space; groups rare destructive actions behind disclosure | ✓ Good | +| Fork rig-core rather than build from scratch | Rig has 20+ LLM providers, typed tools, WASM-compat traits. Reimplementing = months of work. Fork ~300-500 lines of platform patches. | ✓ Good — 7 patches, all isolated | +| Option B (thin fork) for MVP, upstream later | Move fast, patches are isolated to platform layer. If upstream accepts, drop the fork. | ✓ Good — FORK_BASIS.md tracks divergence | +| Sequential tool execution for WASI MVP | Single-threaded sandbox; concurrent tool calls add complexity without benefit | ✓ Good | +| AtomicBool stub for PauseControl | Streaming not used in WASI; full channel replacement is unnecessary | ✓ Good | +| WavsMemory char/4 token heuristic | No tokenizer dep in WASM; approximation is sufficient for budget enforcement | ⚠️ Revisit when accuracy matters | +| P7 anthropic provider un-gate | Only un-gates anthropic (streaming stubbed); other 19 providers stay native-only | ✓ Good for MVP | + +## Evolution + +This document evolves at phase transitions and milestone boundaries. + +**After each phase transition** (via `/gsd-transition`): +1. Requirements invalidated? → Move to Out of Scope with reason +2. Requirements validated? → Move to Validated with phase reference +3. New requirements emerged? → Add to Active +4. Decisions to log? → Add to Key Decisions +5. "What This Is" still accurate? → Update if drifted + +**After each milestone** (via `/gsd-complete-milestone`): +1. Full review of all sections +2. Core Value check — still the right priority? +3. Audit Out of Scope — reasons still valid? +4. Update Context with current state + +--- +*Last updated: 2026-04-22 after v3.0 milestone start* diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md new file mode 100644 index 000000000..126b979f3 --- /dev/null +++ b/.planning/REQUIREMENTS.md @@ -0,0 +1,99 @@ +# Requirements: WAVS Agent Composition + +**Defined:** 2026-04-22 +**Core Value:** Developers can write an autonomous LLM agent in ~30 lines of Rust, compile it to WASM, deploy it as a WAVS service, and have it reason + act on triggers with full sandbox and cryptographic trust guarantees. + +## v3.0 Requirements + +Requirements for agent composition milestone. Each maps to roadmap phases. + +### WIT Interface & Types (Foundation) + +- [ ] **WIT-01**: `operator.wit` exports a new `run-agent` function returning `result` where `step-result` is a variant with `done(list)` and `continue(string)` — backward-compatible with existing `run` export +- [ ] **WIT-02**: `call-service` host import added to operator world — takes service ID + payload bytes, returns result bytes synchronously +- [ ] **WIT-03**: `AllowedServiceCalls` type (All/Only/None) added to `Permissions` in service config with serde default `None` +- [ ] **WIT-04**: `AllowedCallers` type added to service config — callee declares which services may call it (default `None`) +- [ ] **WIT-05**: `max_continuation_steps` field added to component config with default of 10 + +### Agent Continuation + +- [ ] **CONT-01**: Engine re-invocation loop in `run_trigger` — calls `execute_operator_step()`, checks Continue/Done, repeats until Done or max steps +- [ ] **CONT-02**: Auto-persist agent state to KV between steps using `continuation:::step:N` key pattern — developer can override via opt-out +- [ ] **CONT-03**: Step limit enforcement — engine terminates agent with clear error when `max_continuation_steps` exceeded +- [ ] **CONT-04**: Developer-defined multi-step workflows — named step sequences with explicit `continue("step_name")` handoffs +- [ ] **CONT-05**: Component LRU pinning between continuation steps — compiled module stays cached across re-invocations + +### Service-to-Service RPC + +- [ ] **RPC-01**: `call-service` host function using `func_wrap_async` — re-entrant `Arc` calls `execute_operator_component` directly +- [ ] **RPC-02**: `AllowedServiceCalls` permission enforcement — engine checks caller's permission before dispatching call +- [ ] **RPC-03**: `AllowedCallers` callee-side enforcement — engine checks callee accepts calls from the caller service +- [ ] **RPC-04**: Call depth limit (default 5) with cycle detection — prevents A→B→A deadlocks and unbounded nesting + +### Integration & Validation + +- [ ] **E2E-04**: Multi-step agent example demonstrating Continue/Done loop with KV-persisted state across steps +- [ ] **E2E-05**: Service composition example — agent calls a utility service via `call-service` and uses the result +- [ ] **E2E-06**: Permission enforcement test — caller without AllowedServiceCalls gets clear error; callee without AllowedCallers rejects call + +## Future Requirements + +Deferred to v3.x or later milestones. + +### Async & Parallel + +- **ASYNC-01**: Async message-passing between services (fire-and-forget, result via trigger) +- **ASYNC-02**: Parallel tool execution within agent steps (requires WASI Preview 3 async) + +### Advanced Composition + +- **COMP-01**: Composable trust-tier calls — call sub-service at on-chain submission tier +- **COMP-02**: Service discovery — components can query available services at runtime + +### Observability + +- **OBS-01**: Continuation step timeline in Tauri activity feed +- **OBS-02**: Call graph visualization for service-to-service chains + +## Out of Scope + +| Feature | Reason | +|---------|--------| +| Async service-to-service | WASI Preview 3 async not stable (April 2026); sync-first strategy | +| Parallel tool execution | Single-threaded WASM sandbox; requires ecosystem maturation | +| Agent-to-agent negotiation | Requires higher-level protocol; establish RPC primitive first | +| Streaming continuation | SSE not available in WASI; poll-based continuation is sufficient | +| Cross-node service calls | v3.0 is intra-node; cross-node requires P2P service discovery | + +## Traceability + +Which phases cover which requirements. Updated during roadmap creation. + +| Requirement | Phase | Status | +|-------------|-------|--------| +| WIT-01 | Phase 20 | Pending | +| WIT-02 | Phase 20 | Pending | +| WIT-03 | Phase 20 | Pending | +| WIT-04 | Phase 20 | Pending | +| WIT-05 | Phase 20 | Pending | +| CONT-01 | Phase 21 | Pending | +| CONT-02 | Phase 21 | Pending | +| CONT-03 | Phase 21 | Pending | +| CONT-04 | Phase 21 | Pending | +| CONT-05 | Phase 21 | Pending | +| RPC-01 | Phase 22 | Pending | +| RPC-02 | Phase 22 | Pending | +| RPC-03 | Phase 22 | Pending | +| RPC-04 | Phase 22 | Pending | +| E2E-04 | Phase 23 | Pending | +| E2E-05 | Phase 23 | Pending | +| E2E-06 | Phase 23 | Pending | + +**Coverage:** +- v3.0 requirements: 17 total +- Mapped to phases: 17 +- Unmapped: 0 + +--- +*Requirements defined: 2026-04-22* +*Last updated: 2026-04-22 after roadmap creation* diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md new file mode 100644 index 000000000..1a0eb1d7c --- /dev/null +++ b/.planning/ROADMAP.md @@ -0,0 +1,111 @@ +# Roadmap: WAVS Improvements + +## Milestones + +- ✅ **v1.0 WAVS Improvements** — Phases 1-6 (shipped 2026-04-07) +- ✅ **v1.1 Open Source AI Providers & Settings UX** — Phases 7-9 (shipped 2026-04-08) +- ✅ **v1.2 Components Explorer** — Phases 10-12 (shipped 2026-04-08) +- ✅ **v1.3 Activity UX & Bug Fixes** — Phases 13-16 (shipped 2026-04-09) +- ✅ **v2.0 Agent Runtime** — Phases 17-19 (shipped 2026-04-20) +- ✅ **v3.0 Agent Composition** — Phases 20-23 (shipped 2026-04-23) + +## Phases + +
+✅ v1.0 WAVS Improvements (Phases 1-6) — SHIPPED 2026-04-07 + +- [x] Phase 1: OCI Component Pull (2/2 plans) — completed 2026-03-24 +- [x] Phase 2: WIT-to-Schema Tooling (2/2 plans) — completed 2026-03-25 +- [x] Phase 3: MCP Execution Interface (3/3 plans) — completed 2026-03-25 +- [x] Phase 4: Rust Event Foundation (1/1 plan) — completed 2026-04-07 +- [x] Phase 5: Settings Decomposition (2/2 plans) — completed 2026-04-07 +- [x] Phase 6: Unified Activity Frontend (2/2 plans) — completed 2026-04-07 + +Full details: `.planning/milestones/v1.0-ROADMAP.md` + +
+ +
+✅ v1.1 Open Source AI Providers & Settings UX (Phases 7-9) — SHIPPED 2026-04-08 + +- [x] Phase 7: Groq & OpenRouter Providers (1/1 plan) — completed 2026-04-08 +- [x] Phase 8: Ollama Provider (1/1 plan) — completed 2026-04-08 +- [x] Phase 9: Settings Scroll Refactor (1/1 plan) — completed 2026-04-08 + +Full details: `.planning/milestones/v1.1-ROADMAP.md` + +
+ +
+✅ v1.2 Components Explorer (Phases 10-12) — SHIPPED 2026-04-08 + +- [x] Phase 10: Backend Commands (1/1 plan) — completed 2026-04-08 +- [x] Phase 11: Component Detail Page (2/2 plans) — completed 2026-04-08 +- [x] Phase 12: Components List Page (1/1 plan) — completed 2026-04-08 + +Full details: `.planning/milestones/v1.2-ROADMAP.md` + +
+ +
+✅ v1.3 Activity UX & Bug Fixes (Phases 13-16) — SHIPPED 2026-04-09 + +- [x] Phase 13: Activity Backend Pipeline (1/1 plan) — completed 2026-04-09 +- [x] Phase 14: Activity Frontend UX (1/1 plan) — completed 2026-04-09 +- [x] Phase 15: Service Restart Reliability (1/1 plan) — completed 2026-04-09 +- [x] Phase 16: Wallet Kebab Menu (1/1 plan) — completed 2026-04-09 + +Full details: `.planning/milestones/v1.3-ROADMAP.md` + +
+ +
+✅ v2.0 Agent Runtime (Phases 17-19) — SHIPPED 2026-04-20 + +- [x] Phase 17: rig-wasi Fork (2/2 plans) — completed 2026-04-20 +- [x] Phase 18: wavs-rig Integration Crate (3/3 plans) — completed 2026-04-20 +- [x] Phase 19: Example Agent & E2E Validation (2/2 plans) — completed 2026-04-20 + +Full details: `.planning/milestones/v2.0-ROADMAP.md` + +
+ +
+✅ v3.0 Agent Composition (Phases 20-23) — SHIPPED 2026-04-23 + +- [x] Phase 20: WIT Interface & Types (2/2 plans) — completed 2026-04-22 +- [x] Phase 21: Agent Continuation Engine (2/2 plans) — completed 2026-04-22 +- [x] Phase 22: Service-to-Service RPC (2/2 plans) — completed 2026-04-22 +- [x] Phase 23: Integration & Validation (2/2 plans) — completed 2026-04-23 + +Full details: `.planning/milestones/v3.0-ROADMAP.md` + +
+ +## Progress + +| Phase | Milestone | Plans Complete | Status | Completed | +|-------|-----------|----------------|--------|-----------| +| 1. OCI Component Pull | v1.0 | 2/2 | Complete | 2026-03-24 | +| 2. WIT-to-Schema Tooling | v1.0 | 2/2 | Complete | 2026-03-25 | +| 3. MCP Execution Interface | v1.0 | 3/3 | Complete | 2026-03-25 | +| 4. Rust Event Foundation | v1.0 | 1/1 | Complete | 2026-04-07 | +| 5. Settings Decomposition | v1.0 | 2/2 | Complete | 2026-04-07 | +| 6. Unified Activity Frontend | v1.0 | 2/2 | Complete | 2026-04-07 | +| 7. Groq & OpenRouter Providers | v1.1 | 1/1 | Complete | 2026-04-08 | +| 8. Ollama Provider | v1.1 | 1/1 | Complete | 2026-04-08 | +| 9. Settings Scroll Refactor | v1.1 | 1/1 | Complete | 2026-04-08 | +| 10. Backend Commands | v1.2 | 1/1 | Complete | 2026-04-08 | +| 11. Component Detail Page | v1.2 | 2/2 | Complete | 2026-04-08 | +| 12. Components List Page | v1.2 | 1/1 | Complete | 2026-04-08 | +| 13. Activity Backend Pipeline | v1.3 | 1/1 | Complete | 2026-04-09 | +| 14. Activity Frontend UX | v1.3 | 1/1 | Complete | 2026-04-09 | +| 15. Service Restart Reliability | v1.3 | 1/1 | Complete | 2026-04-09 | +| 16. Wallet Kebab Menu | v1.3 | 1/1 | Complete | 2026-04-09 | +| 17. rig-wasi Fork | v2.0 | 2/2 | Complete | 2026-04-20 | +| 18. wavs-rig Integration Crate | v2.0 | 3/3 | Complete | 2026-04-20 | +| 19. Example Agent & E2E Validation | v2.0 | 2/2 | Complete | 2026-04-20 | +| 20. WIT Interface & Types | v3.0 | 2/2 | Complete | 2026-04-22 | +| 21. Agent Continuation Engine | v3.0 | 2/2 | Complete | 2026-04-22 | +| 22. Service-to-Service RPC | v3.0 | 2/2 | Complete | 2026-04-22 | +| 23. Integration & Validation | v3.0 | 2/2 | Complete | 2026-04-23 | diff --git a/.planning/STATE.md b/.planning/STATE.md new file mode 100644 index 000000000..0d37c18a7 --- /dev/null +++ b/.planning/STATE.md @@ -0,0 +1,60 @@ +--- +gsd_state_version: 1.0 +milestone: v3.0 +milestone_name: Agent Composition +status: executing +stopped_at: Roadmap created — Phase 20 ready to plan +last_updated: "2026-04-23T14:28:01.253Z" +last_activity: 2026-04-23 +progress: + total_phases: 4 + completed_phases: 4 + total_plans: 8 + completed_plans: 8 + percent: 100 +--- + +# Project State + +## Project Reference + +See: .planning/PROJECT.md (updated 2026-04-22) + +**Core value:** Developers can write an autonomous LLM agent in ~30 lines of Rust, compile it to WASM, deploy it as a WAVS service, and have it reason + act on triggers with full sandbox and cryptographic trust guarantees. +**Current focus:** Phase 23 — Integration & Validation + +## Current Position + +Phase: 23 +Plan: Not started +Status: Executing Phase 23 +Last activity: 2026-04-23 + +Progress: [░░░░░░░░░░] 0% + +## Accumulated Context + +### Decisions + +- v2.0: Fork rig-core (Option B — thin fork); ~7 patches, all isolated +- v2.0: Sequential tool execution for WASI MVP +- v2.0: AtomicBool stub for PauseControl (streaming not used in WASI) +- v3.0: WIT strategy — additive `run-agent` export alongside existing `run`; backward-compatible with all components at wavs:operator@2.7.0 +- v3.0: State persistence — KV-backed only (`wavs_agent_step:` prefix); `Continue` return carries key string only, not inline state (avoids 4KB cap) +- v3.0: `call-service` — must use `func_wrap_async`; re-entrant `Arc`, never route through Dispatcher channel +- v3.0: Security invariants ship with features — step limits with Phase 21, cycle detection + AllowedServiceCalls/AllowedCallers with Phase 22 + +### Pending Todos + +None yet. + +### Blockers/Concerns + +- **Phase 22:** Verify `execute_operator_component` can be called re-entrantly within the same Tokio task without Wasmtime Store aliasing violations (Wasmtime issue #9600) — validate before Phase 22 implementation +- **Multi-operator agents:** LLM continuation agents require temperature=0 for deterministic consensus across operators; must be documented as a deployment constraint + +## Session Continuity + +Last session: 2026-04-22 +Stopped at: Roadmap created — Phase 20 ready to plan +Resume file: None diff --git a/.planning/config.json b/.planning/config.json new file mode 100644 index 000000000..33880a43b --- /dev/null +++ b/.planning/config.json @@ -0,0 +1,15 @@ +{ + "mode": "yolo", + "granularity": "coarse", + "parallelization": true, + "commit_docs": true, + "model_profile": "balanced", + "workflow": { + "research": true, + "plan_check": true, + "verifier": true, + "nyquist_validation": false, + "auto_advance": false, + "_auto_chain_active": false + } +} \ No newline at end of file diff --git a/.planning/milestones/v1.0-MILESTONE-AUDIT.md b/.planning/milestones/v1.0-MILESTONE-AUDIT.md new file mode 100644 index 000000000..60a99c89c --- /dev/null +++ b/.planning/milestones/v1.0-MILESTONE-AUDIT.md @@ -0,0 +1,104 @@ +--- +milestone: v1.0 +audited: 2026-04-07 +status: tech_debt +scores: + requirements: 13/13 + phases: 6/6 + integration: 14/14 + flows: 3/3 +gaps: + requirements: [] + integration: [] + flows: [] +tech_debt: + - phase: 01-oci-component-pull + items: + - "OCI-01 through OCI-06 checkboxes not marked [x] in REQUIREMENTS.md (code complete per VERIFICATION.md)" + - phase: 02-wit-to-schema-tooling + items: + - "No VERIFICATION.md — phase completed before GSD verification was integrated" + - "SCHEMA-01 through SCHEMA-05 checkboxes not marked [x] in REQUIREMENTS.md" + - phase: 03-mcp-execution-interface + items: + - "No VERIFICATION.md — phase completed before GSD verification was integrated" + - "EXEC-03 and EXEC-04 not marked [x] in REQUIREMENTS.md" + - phase: 04-rust-event-foundation + items: + - "EVT-01, ERR-01 requirement IDs exist only in ROADMAP.md, not in REQUIREMENTS.md traceability table" + - phase: 05-settings-decomposition + items: + - "SET-01 through SET-06 requirement IDs exist only in ROADMAP.md, not in REQUIREMENTS.md" + - "Human visual verification deferred (sidebar layout, banner positioning)" + - phase: 06-unified-activity-frontend + items: + - "EVT-02 through ERR-04 requirement IDs exist only in ROADMAP.md, not in REQUIREMENTS.md" + - "Human visual verification deferred (filter tabs, failed card, service activity tab)" + - "ERR-04 partial: ActivityCard orphan path has truncate class on error div (GroupedActivityCard is clean)" + - "Minor type widening: McpSection accepts boolean|null for mcp_auto_start but Settings passes boolean" +--- + +# Milestone v1.0 — Audit Report + +## Summary + +| Dimension | Score | Status | +|-----------|-------|--------| +| Requirements | 13/13 | All implemented | +| Phases | 6/6 | All complete | +| Integration | 14/14 | All wired | +| E2E Flows | 3/3 | All connected | + +**Overall:** tech_debt — all requirements implemented, no critical gaps, but accumulated documentation debt and deferred human verification. + +## Phase Results + +| Phase | Verification | Status | Notes | +|-------|-------------|--------|-------| +| 1. OCI Component Pull | passed (human_needed) | Complete | Human UAT items tracked | +| 2. WIT-to-Schema Tooling | NO FILE | Complete | Pre-dates GSD verification | +| 3. MCP Execution Interface | NO FILE | Complete | Pre-dates GSD verification | +| 4. Rust Event Foundation | passed | Complete | Full automated pass | +| 5. Settings Decomposition | human_needed | Complete | 2 visual items deferred | +| 6. Unified Activity Frontend | human_needed | Complete | 4 visual items deferred | + +## E2E Flow Verification + +| Flow | Status | Trace | +|------|--------|-------| +| Trigger → correlation_id → frontend → grouped feed | ✓ WIRED | trigger.rs → TriggerAction → TriggerEvent → listeners.ts → appStore → useGroupedActivity → GroupedActivityCard | +| Submission failure → SubmissionFailed → red dot | ✓ WIRED | submission.rs (2 sites) → DispatcherCommand → dispatcher → SubmissionFailedEvent → listeners.ts → appStore → GroupedActivityCard (animate-glow-red) | +| Settings sidebar → sections → OAuth survival | ✓ WIRED | Settings.tsx parent holds listener → AgentSection receives props → section unmount doesn't destroy listener | + +## Requirements Coverage + +### v1 Requirements (REQUIREMENTS.md) + +| ID | Description | Phase | Code Status | REQUIREMENTS.md | +|----|-------------|-------|-------------|-----------------| +| OCI-01-06 | OCI component pull | 1 | Implemented | Checkboxes unmarked | +| SCHEMA-01-05 | WIT-to-schema tooling | 2 | Implemented | Checkboxes unmarked | +| EXEC-01-08 | MCP execution interface | 3 | Partially implemented (03,04 pending) | Mixed | + +### Extended Requirements (ROADMAP-only) + +| ID | Description | Phase | Code Status | +|----|-------------|-------|-------------| +| EVT-01 | Correlation ID on events | 4 | ✓ Verified | +| ERR-01 | Submission failure surfacing | 4 | ✓ Verified | +| SET-01-06 | Settings decomposition | 5 | ✓ Verified (human deferred) | +| EVT-02-05 | Unified activity frontend | 6 | ✓ Verified (human deferred) | +| ERR-02-04 | Error display in feed | 6 | ✓ Verified (ERR-04 partial on orphan path) | + +## Tech Debt Summary + +**Total: 11 items across 6 phases** + +1. **Documentation debt:** REQUIREMENTS.md checkboxes not updated for phases 1-3; phases 4-6 requirements not added to REQUIREMENTS.md +2. **Missing verification:** Phases 2, 3 have no VERIFICATION.md +3. **Deferred human testing:** 6 visual verification items across phases 5 and 6 +4. **Minor code issues:** ERR-04 orphan path truncation, McpSection type widening + +## Recommendation + +No critical gaps. All code is implemented and cross-phase wiring is verified. Proceed to `/gsd-complete-milestone` and track tech debt in backlog. diff --git a/.planning/milestones/v1.0-REQUIREMENTS.md b/.planning/milestones/v1.0-REQUIREMENTS.md new file mode 100644 index 000000000..c3f2fa364 --- /dev/null +++ b/.planning/milestones/v1.0-REQUIREMENTS.md @@ -0,0 +1,102 @@ +# Requirements Archive: v1.0 WAVS Improvements + +**Archived:** 2026-04-07 +**Status:** SHIPPED + +For current requirements, see `.planning/REQUIREMENTS.md`. + +--- + +# Requirements: WAVS Improvements + +**Defined:** 2026-03-24 +**Core Value:** AI agent developers can use WAVS components as MCP tools with the same ease as Wassette, but with cryptographic trust guarantees Wassette structurally cannot provide. + +## v1 Requirements + +### WIT-to-Schema + +- [ ] **SCHEMA-01**: Developer can run `wavs wit-schema ` to generate JSON Schema from a compiled component +- [ ] **SCHEMA-02**: WIT primitive types map to JSON Schema (`u32/u64` → integer, `string` → string, `bool` → boolean, `option` → nullable) +- [ ] **SCHEMA-03**: WIT record and enum/variant types map to JSON Schema objects and `oneOf` +- [ ] **SCHEMA-04**: WIT doc comments are embedded as JSON Schema `description` fields +- [ ] **SCHEMA-05**: Generated schemas are cached by component SHA256 digest (skip re-parsing unchanged binaries) + +### MCP Execution + +- [x] **EXEC-01**: Deployed service components appear as callable MCP tools via `tools/list` +- [x] **EXEC-02**: Agent can call a component via `tools/call` and receive execution result (Tier 1: result only) +- [ ] **EXEC-03**: Agent can request signed result with operator signature proving authenticity (Tier 2) +- [ ] **EXEC-04**: Agent can request on-chain submission with transaction hash (Tier 3), gated by service-level flag in service.json +- [x] **EXEC-05**: Trust tier is an explicit `inputSchema` parameter on each tool (not parallel tools) +- [x] **EXEC-06**: MCP `notifications/tools/list_changed` fires when services are deployed or removed +- [x] **EXEC-07**: Execution tools are guarded by `--exec-enabled` flag and use `wavs_exec_` naming prefix +- [x] **EXEC-08**: Per-call timeout cap (25s) enforced at MCP layer, independent of component time limit + +### OCI Distribution + +- [ ] **OCI-01**: `service.json` accepts `oci://` URIs as component source +- [ ] **OCI-02**: Components are pulled from OCI registries at service deploy time +- [ ] **OCI-03**: Pulled components are verified by SHA256 digest before loading +- [ ] **OCI-04**: Pulled components are cached on disk by digest (no re-pull for identical content) +- [ ] **OCI-05**: Digest pinning (`@sha256:`) is supported; deploy warns if only tag is specified +- [ ] **OCI-06**: Authenticated pull supported via environment credentials for private registries + +## v2 Requirements + +### Authentication & Authorization + +- **AUTH-01**: MCP HTTP transport uses ERC-8128/RFC 9421 signed requests for wallet-based authentication +- **AUTH-02**: Server recovers Ethereum address from ECDSA signature and checks per-tool authorization +- **AUTH-03**: Replay protection via TTL + optional nonce for high-value operations (Tier 3) +- **AUTH-04**: ERC-8004 on-chain identity/reputation registry integration for agent authorization + +### Advanced Features + +- **ADV-01**: OCI component publishing tooling (`wavs oci push`) +- **ADV-02**: WIT resource type support in schema generation +- **ADV-03**: Multi-operator quorum signing for Tier 2 (aggregate signatures from multiple operators) + +## Out of Scope + +| Feature | Reason | +|---------|--------| +| Demo/doc the `Only` allowlist variant | Tracked separately, different repo | +| Wassette feature parity docs/marketing | Marketing concern, not code | +| Tauri desktop app changes | This milestone is platform/MCP focused | +| MCP stdio transport signing | Stdio is local-process; trust boundary is machine-level, not network-level | +| Custom OCI media types | Must follow CNCF spec for Wassette ecosystem compatibility | +| Blocking node startup for OCI pulls | Pull at deploy time, not boot time | + +## Traceability + +| Requirement | Phase | Status | +|-------------|-------|--------| +| SCHEMA-01 | Phase 2 | Pending | +| SCHEMA-02 | Phase 2 | Pending | +| SCHEMA-03 | Phase 2 | Pending | +| SCHEMA-04 | Phase 2 | Pending | +| SCHEMA-05 | Phase 2 | Pending | +| EXEC-01 | Phase 3 | Complete (03-02) | +| EXEC-02 | Phase 3 | Complete (03-02) | +| EXEC-03 | Phase 3 | Pending | +| EXEC-04 | Phase 3 | Pending | +| EXEC-05 | Phase 3 | Complete (03-01) | +| EXEC-06 | Phase 3 | Complete (03-02) | +| EXEC-07 | Phase 3 | Complete (03-01) | +| EXEC-08 | Phase 3 | Complete (03-01) | +| OCI-01 | Phase 1 | Pending | +| OCI-02 | Phase 1 | Pending | +| OCI-03 | Phase 1 | Pending | +| OCI-04 | Phase 1 | Pending | +| OCI-05 | Phase 1 | Pending | +| OCI-06 | Phase 1 | Pending | + +**Coverage:** +- v1 requirements: 19 total +- Mapped to phases: 19 +- Unmapped: 0 + +--- +*Requirements defined: 2026-03-24* +*Last updated: 2026-03-24 after roadmap creation (traceability populated)* diff --git a/.planning/milestones/v1.0-ROADMAP.md b/.planning/milestones/v1.0-ROADMAP.md new file mode 100644 index 000000000..acbe70d96 --- /dev/null +++ b/.planning/milestones/v1.0-ROADMAP.md @@ -0,0 +1,125 @@ +# Roadmap: WAVS Improvements + +## Overview + +Three capability extensions to the WAVS platform — OCI component distribution, WIT-to-schema tooling, and an MCP execution interface — that position WAVS as a cryptographically verifiable upgrade path from Microsoft Wassette for AI agent developers. OCI pull ships first because it is independent and enables the rest of testing to use real registry-hosted components. WIT-to-schema ships second because MCP execution tools require generated `inputSchema` and `outputSchema` fields. MCP execution ships last and combines all three trust tiers in one phase since WAVS already has the submission pipeline. + +## Phases + +**Phase Numbering:** +- Integer phases (1, 2, 3): Planned milestone work +- Decimal phases (2.1, 2.2): Urgent insertions (marked with INSERTED) + +Decimal phases appear between their surrounding integers in numeric order. + +- [x] **Phase 1: OCI Component Pull** - Service definitions accept `oci://` URIs; components are pulled, verified, and cached at deploy time +- [x] **Phase 2: WIT-to-Schema Tooling** - Developer can inspect any compiled WASM component and get a JSON Schema describing its interface +- [ ] **Phase 3: MCP Execution Interface** - Deployed service components appear as callable MCP tools with three explicit trust tiers +- [x] **Phase 4: Rust Event Foundation** - Correlation IDs on trigger/submission events and submission failure surfacing to the GUI +- [ ] **Phase 5: Settings Decomposition** - Settings page restructured into sidebar-navigated layout with isolated section components +- [ ] **Phase 6: Unified Activity Frontend** - Activity feed displays triggers and submissions as nested parent-child events with error surfacing + +## Phase Details + +### Phase 1: OCI Component Pull +**Goal**: Developers can deploy WAVS services that reference OCI-hosted WASM components by URI, with digest-verified pull and content-addressed caching +**Depends on**: Nothing (first phase) +**Requirements**: OCI-01, OCI-02, OCI-03, OCI-04, OCI-05, OCI-06 +**Success Criteria** (what must be TRUE): + 1. A `service.json` with an `oci://ghcr.io/...` component URI deploys successfully without requiring a local `.wasm` file + 2. WAVS refuses to deploy a service whose pulled component does not match the declared `@sha256:` digest + 3. Deploying the same service twice does not re-pull the component from the registry (cache hit confirmed in logs) + 4. A deploy using only a mutable tag (no `@sha256:` pin) emits a visible warning before proceeding + 5. Pulling from a private registry succeeds when credentials are provided via environment variables +**Plans**: 2 plans +Plans: +- [x] 01-01-PLAN.md — Add ComponentSource::Oci type variant and create OCI puller module +- [x] 01-02-PLAN.md — Wire OCI pull into engine, fix digest() Option callers, full integration + +### Phase 2: WIT-to-Schema Tooling +**Goal**: Developers and the MCP execution layer can retrieve a machine-readable JSON Schema describing the input and output types of any compiled WASM component +**Depends on**: Phase 1 +**Requirements**: SCHEMA-01, SCHEMA-02, SCHEMA-03, SCHEMA-04, SCHEMA-05 +**Success Criteria** (what must be TRUE): + 1. Running `wavs wit-schema ` on any compiled WAVS component prints a valid JSON Schema to stdout + 2. A component whose WIT interface uses primitives (`u32`, `string`, `bool`, `option`) produces a schema with correct JSON Schema type mappings + 3. A component with WIT record and enum/variant types produces a schema with `object` and `oneOf` entries including a required discriminator field + 4. WIT doc comments on functions and types appear as `description` fields in the generated schema + 5. Running the schema command twice on the same unchanged binary takes measurably less time than the first run (cache hit) +**Plans**: 2 plans +Plans: +- [x] 02-01-PLAN.md — Create wit-schema library crate with core type conversion, traversal, cache, and doc enrichment +- [x] 02-02-PLAN.md — Wire CLI command into wavs-cli, end-to-end verification with real components + +### Phase 3: MCP Execution Interface +**Goal**: AI agents can discover and invoke deployed WAVS service components as MCP tools, choosing an explicit trust tier per call — from raw result through cryptographically signed result to on-chain submission +**Depends on**: Phase 2 +**Requirements**: EXEC-01, EXEC-02, EXEC-03, EXEC-04, EXEC-05, EXEC-06, EXEC-07, EXEC-08 +**Success Criteria** (what must be TRUE): + 1. An MCP client calling `tools/list` sees one `wavs_exec_` tool per deployed service workflow, with a populated `inputSchema` including trust_tier and timeout_ms parameters + 2. An agent calling `tools/call` with `trust_tier: "result_only"` receives the component execution output within 25 seconds or a structured timeout error + 3. An agent calling with `trust_tier: "signed_result"` receives a response envelope containing the result, operator signature, and signer public key + 4. An agent calling with `trust_tier: "on_chain"` receives a gas estimate on the first call and a submission result on the confirmation call, gated by `--exec-enabled` flag and service submit config + 5. Deploying or removing a service causes `notifications/tools/list_changed` to fire so agents discover tool changes without reconnecting +**Plans**: 3 plans +Plans: +- [ ] 03-01-PLAN.md — Execution foundation: exec types, errors, schema merging, service cache, --exec-enabled flag, /dev/execute node endpoint +- [ ] 03-02-PLAN.md — Dynamic tool discovery and Tier 1 execution: list_tools merge, call_tool dispatch, timeout, notifications +- [ ] 03-03-PLAN.md — Tier 2 signed_result signing and Tier 3 on_chain two-step estimate-then-submit + +### Phase 4: Rust Event Foundation +**Goal**: The WAVS backend emits a correlation ID on every trigger and submission event, and surfaces submission failures to the GUI +**Depends on**: Nothing (independent infrastructure phase) +**Requirements**: EVT-01, ERR-01 +**Success Criteria** (what must be TRUE): + 1. Every TriggerEvent and SubmissionEvent reaching the desktop app includes a correlation_id that uniquely identifies the trigger execution and links a trigger to its submission + 2. When a submission fails (signing error or dispatch error), a SubmissionFailedEvent reaches the GUI with an error message and correlation_id +**Plans**: 1 plans +Plans: +- [x] 04-01-PLAN.md — Add correlation_id to TriggerAction, SubmissionFailed event path, and TypeScript type mirroring + +### Phase 5: Settings Decomposition +**Goal**: The Settings page is restructured into a sidebar-navigated layout with each section extracted into an isolated component, without breaking OAuth flows or the unsaved-changes banner +**Depends on**: Phase 3 (independent of Phase 4; can run in parallel) +**Requirements**: SET-01, SET-02, SET-03, SET-04, SET-05, SET-06 +**Success Criteria** (what must be TRUE): + 1. The Settings page displays a sidebar with labeled items for all sections; clicking an item shows only that section's content + 2. The currently active section is visually distinguished in the sidebar + 3. The restart / unsaved-changes banner remains visible at all times regardless of which section is selected + 4. An OAuth agent API key flow that spans a redirect-and-callback survives navigating between sidebar sections without losing its listener + 5. Each settings section (Wallet, Node, Env Vars, Agent, MCP, Reset) is an isolated component; no section directly reads another section's local state +**Plans**: 2 plans +Plans: +- [x] 05-01-PLAN.md — Create SettingsSidebar, extract Wallet/Node/Environment sections, rewrite Settings.tsx shell with sidebar layout +- [x] 05-02-PLAN.md — Extract Agent/MCP/Reset sections, finalize Settings.tsx as minimal orchestrating shell +**UI hint**: yes + +### Phase 6: Unified Activity Frontend +**Goal**: The activity feed on both the Activity page and the Service detail tab displays triggers and submissions as nested parent-child events, shows inline error messages for failed submissions, and replaces the kind-filter tabs with event-appropriate filtering +**Depends on**: Phase 4 +**Requirements**: EVT-02, EVT-03, EVT-04, EVT-05, ERR-02, ERR-03, ERR-04 +**Success Criteria** (what must be TRUE): + 1. A trigger with a completed submission appears as a single expandable card; expanding it reveals the submission result nested underneath + 2. A trigger whose submission has not yet arrived shows a visible pending/in-flight indicator on its card + 3. A failed submission shows an error badge on the collapsed card and the full error message when expanded + 4. Failed events are never automatically removed from the activity feed; successful events follow existing retention behavior + 5. The unified event model (nested submissions, pending states, error badges) is present on both the standalone Activity page and the per-service activity tab +**Plans**: 2 plans +Plans: +- [x] 06-01-PLAN.md — Data layer: GroupedActivityEvent type, useGroupedActivity hook, appStore eviction guard, status filter types +- [x] 06-02-PLAN.md — UI layer: GroupedActivityCard component, ActivityFeed refactor with status tabs and grouped virtualizer +**UI hint**: yes + +## Progress + +**Execution Order:** +Phases execute in numeric order: 1 → 2 → 3 + +| Phase | Plans Complete | Status | Completed | +|-------|----------------|--------|-----------| +| 1. OCI Component Pull | 2/2 | Complete | 2026-03-24 | +| 2. WIT-to-Schema Tooling | 2/2 | Complete | 2026-03-25 | +| 3. MCP Execution Interface | 0/3 | In progress | - | +| 4. Rust Event Foundation | 1/1 | Complete | 2026-04-07 | +| 5. Settings Decomposition | 0/2 | Not started | - | +| 6. Unified Activity Frontend | 0/2 | Not started | - | diff --git a/.planning/milestones/v1.1-MILESTONE-AUDIT.md b/.planning/milestones/v1.1-MILESTONE-AUDIT.md new file mode 100644 index 000000000..e8d6c2f3e --- /dev/null +++ b/.planning/milestones/v1.1-MILESTONE-AUDIT.md @@ -0,0 +1,83 @@ +--- +milestone: v1.1 +audited: 2026-04-08 +status: tech_debt +scores: + requirements: 10/10 + phases: 3/3 + integration: 9/10 + flows: 4/4 +gaps: + requirements: [] + integration: + - id: "INT-01" + description: "Duplicate h2 headings in settings sections (cosmetic)" + affected_requirements: [] + severity: "cosmetic" + phase: "09" + flows: [] +tech_debt: + - phase: 07-groq-openrouter-providers + items: + - "Human verification deferred: provider dropdown order, API key save/mask, sidecar restart with new provider" + - phase: 08-ollama-provider + items: + - "Human verification deferred: Ollama UI behavior, end-to-end tool calling with live Ollama" + - "FIXED: appStore.ts missing agent_base_url initial state (commit 7d17cdc0)" + - phase: 09-settings-scroll-refactor + items: + - "Human verification deferred: sidebar scroll tracking, click-to-scroll, OAuth listener persistence, restart banner" + - "Cosmetic: duplicate h2 headings per section (Settings.tsx wrapper + component internal)" +--- + +# Milestone v1.1 Audit — Open Source AI Providers & Settings UX + +## Requirements Coverage + +| Requirement | Phase | Verification | Summary | Status | +|-------------|-------|-------------|---------|--------| +| PROV-01 | 7 | SATISFIED | Groq in dropdown | satisfied | +| PROV-02 | 7 | SATISFIED | OpenRouter in dropdown | satisfied | +| PROV-03 | 7 | SATISFIED | API keys for Groq/OpenRouter | satisfied | +| PROV-04 | 8 | SATISFIED | Ollama in dropdown | satisfied | +| PROV-05 | 8 | SATISFIED | Base URL for Ollama | satisfied | +| PROV-06 | 8 | SATISFIED | Sidecar loads models.json | satisfied | +| PROV-07 | 8 | NEEDS HUMAN | Ollama tool calling e2e | satisfied (code) | +| UX-01 | 9 | SATISFIED | Scroll all sections | satisfied | +| UX-02 | 9 | SATISFIED (code) | Sidebar highlights visible section | satisfied (code) | +| UX-03 | 9 | SATISFIED (code) | Click sidebar to scroll | satisfied (code) | + +**Score: 10/10 requirements satisfied at code level** + +## Phase Summary + +| Phase | Plans | Status | Human Items | +|-------|-------|--------|-------------| +| 7: Groq & OpenRouter Providers | 1/1 | Complete | 3 deferred | +| 8: Ollama Provider | 1/1 | Complete | 2 deferred | +| 9: Settings Scroll Refactor | 1/1 | Complete | 4 deferred | + +## Integration Check + +- 14 cross-phase exports properly wired +- 0 orphaned exports +- 1 build error found and fixed (appStore.ts missing agent_base_url) +- 1 cosmetic issue (duplicate h2 headings in settings sections) +- All 4 E2E flows verified: Groq setup, OpenRouter setup, Ollama setup, Settings scroll + +## Tech Debt + +### Phase 7 +- Human verification deferred for dropdown order, API key flows, sidecar restart + +### Phase 8 +- Human verification deferred for Ollama UI and tool calling +- Build error fixed during audit (appStore.ts) + +### Phase 9 +- Human verification deferred for scroll behavior +- Duplicate h2 headings (cosmetic — Settings.tsx wrapper + section internal) + +## Conclusion + +All 10 requirements are satisfied at the code level. No critical blockers. 9 human verification items deferred across 3 phases — these are visual/behavioral confirmations that require a running app. 1 cosmetic issue (duplicate headings) is non-blocking. diff --git a/.planning/milestones/v1.1-REQUIREMENTS.md b/.planning/milestones/v1.1-REQUIREMENTS.md new file mode 100644 index 000000000..c390aba2b --- /dev/null +++ b/.planning/milestones/v1.1-REQUIREMENTS.md @@ -0,0 +1,82 @@ +# Requirements Archive: v1.1 Open Source AI Providers & Settings UX + +**Archived:** 2026-04-08 +**Status:** SHIPPED + +For current requirements, see `.planning/REQUIREMENTS.md`. + +--- + +# Requirements: WAVS Improvements + +**Defined:** 2026-04-08 +**Core Value:** AI agent developers can use WAVS components as MCP tools with the same ease as Wassette, but with cryptographic trust guarantees Wassette structurally cannot provide. + +## v1.1 Requirements + +Requirements for milestone v1.1: Open Source AI Providers & Settings UX. + +### Agent Providers + +- [ ] **PROV-01**: User can select Groq as an agent provider from the settings dropdown +- [ ] **PROV-02**: User can select OpenRouter as an agent provider from the settings dropdown +- [ ] **PROV-03**: User can configure API keys for Groq and OpenRouter providers +- [ ] **PROV-04**: User can select Ollama as an agent provider from the settings dropdown +- [ ] **PROV-05**: User can configure a base URL for Ollama (defaults to localhost:11434) +- [ ] **PROV-06**: Agent sidecar loads custom provider config from models.json at startup +- [ ] **PROV-07**: User can use the agent with Ollama-hosted open-source models for WAVS tasks + +### Settings Layout + +- [ ] **UX-01**: User can scroll through all settings sections on a single page +- [ ] **UX-02**: Sidebar highlights the currently visible section as user scrolls +- [ ] **UX-03**: User can click a sidebar item to scroll to that section + +## v2 Requirements + +Deferred to future release. Tracked but not in current roadmap. + +### Agent Providers + +- **PROV-08**: User can select Together AI as an agent provider +- **PROV-09**: Thinking level selector is hidden for providers that don't support it +- **PROV-10**: User can add fully custom OpenAI-compatible providers with arbitrary base URLs + +### Environment Variables + +- **ENV-01**: Environment variable suggestions grouped by category (AI, Blockchain, Storage) + +## Out of Scope + +| Feature | Reason | +|---------|--------| +| Dynamic model list fetch from providers | Anti-feature per research — adds async failure states for marginal benefit | +| Connection test button in settings | Same as above — runtime errors are sufficient feedback | +| Together AI provider | Deferred to v2 — not a KnownProvider, needs same models.json plumbing as Ollama | +| Env var category grouping | Current flat chip list works fine per user preference | + +## Traceability + +Which phases cover which requirements. Updated during roadmap creation. + +| Requirement | Phase | Status | +|-------------|-------|--------| +| PROV-01 | Phase 7 | Pending | +| PROV-02 | Phase 7 | Pending | +| PROV-03 | Phase 7 | Pending | +| PROV-04 | Phase 8 | Pending | +| PROV-05 | Phase 8 | Pending | +| PROV-06 | Phase 8 | Pending | +| PROV-07 | Phase 8 | Pending | +| UX-01 | Phase 9 | Pending | +| UX-02 | Phase 9 | Pending | +| UX-03 | Phase 9 | Pending | + +**Coverage:** +- v1.1 requirements: 10 total +- Mapped to phases: 10 +- Unmapped: 0 + +--- +*Requirements defined: 2026-04-08* +*Last updated: 2026-04-08 after roadmap creation* diff --git a/.planning/milestones/v1.1-ROADMAP.md b/.planning/milestones/v1.1-ROADMAP.md new file mode 100644 index 000000000..0caaa050f --- /dev/null +++ b/.planning/milestones/v1.1-ROADMAP.md @@ -0,0 +1,88 @@ +# Roadmap: WAVS Improvements + +## Milestones + +- ✅ **v1.0 WAVS Improvements** — Phases 1-6 (shipped 2026-04-07) +- 🚧 **v1.1 Open Source AI Providers & Settings UX** — Phases 7-9 (in progress) + +## Phases + +
+✅ v1.0 WAVS Improvements (Phases 1-6) — SHIPPED 2026-04-07 + +- [x] Phase 1: OCI Component Pull (2/2 plans) — completed 2026-03-24 +- [x] Phase 2: WIT-to-Schema Tooling (2/2 plans) — completed 2026-03-25 +- [x] Phase 3: MCP Execution Interface (3/3 plans) — completed 2026-03-25 +- [x] Phase 4: Rust Event Foundation (1/1 plan) — completed 2026-04-07 +- [x] Phase 5: Settings Decomposition (2/2 plans) — completed 2026-04-07 +- [x] Phase 6: Unified Activity Frontend (2/2 plans) — completed 2026-04-07 + +Full details: `.planning/milestones/v1.0-ROADMAP.md` + +
+ +### 🚧 v1.1 Open Source AI Providers & Settings UX (In Progress) + +**Milestone Goal:** Let users configure open-source AI models (Groq, OpenRouter, Ollama) as agent providers, with proper persistence through the Rust backend and sidecar, and convert the settings page to a scrollable single-page layout with sidebar anchor navigation. + +- [ ] **Phase 7: Groq & OpenRouter Providers** - Backend schema + Groq/OpenRouter working end-to-end +- [ ] **Phase 8: Ollama Provider** - models.json plumbing, sidecar switch, Ollama working end-to-end +- [ ] **Phase 9: Settings Scroll Refactor** - Single-page scrollable settings with sidebar anchor navigation + +## Phase Details + +### Phase 7: Groq & OpenRouter Providers +**Goal**: Users can select and configure Groq and OpenRouter as agent providers, with credentials persisted and the agent using those providers immediately after a restart +**Depends on**: Phase 6 (v1.0 complete — settings decomposition exists) +**Requirements**: PROV-01, PROV-02, PROV-03 +**Success Criteria** (what must be TRUE): + 1. User can select Groq from the provider dropdown in agent settings + 2. User can select OpenRouter from the provider dropdown in agent settings + 3. User can enter and save an API key for Groq and OpenRouter; credentials persist across app restarts + 4. After saving and restarting, the agent sidecar uses the selected provider for responses +**Plans**: 1 plan +Plans: +- [x] 07-01-PLAN.md — Add Groq & OpenRouter providers to UI dropdown and sidecar startup +**UI hint**: yes + +### Phase 8: Ollama Provider +**Goal**: Users can configure Ollama as an agent provider with a custom base URL, and the agent works end-to-end with locally-hosted open-source models including tool-calling tasks +**Depends on**: Phase 7 +**Requirements**: PROV-04, PROV-05, PROV-06, PROV-07 +**Success Criteria** (what must be TRUE): + 1. User can select Ollama from the provider dropdown in agent settings + 2. User can set a base URL for Ollama (pre-filled with localhost:11434); the field appears only when Ollama is selected + 3. After saving and restarting, the agent sidecar loads Ollama from models.json and resolves the provider correctly + 4. User can complete a WAVS task (e.g., "list services") using an Ollama-hosted model — tool calling works end-to-end +**Plans**: 1 plan +Plans: +- [x] 08-01-PLAN.md — Add Ollama provider with base URL field, models.json generation, and ModelRegistry.create() sidecar switch +**UI hint**: yes + +### Phase 9: Settings Scroll Refactor +**Goal**: Users can navigate all settings sections on a single scrollable page with the sidebar tracking position and supporting click-to-scroll +**Depends on**: Phase 6 (v1.0 complete — Settings.tsx decomposed into sections) +**Requirements**: UX-01, UX-02, UX-03 +**Success Criteria** (what must be TRUE): + 1. User can scroll through all settings sections without switching tabs or triggering navigation + 2. The sidebar highlights the section currently visible in the viewport as the user scrolls + 3. User can click any sidebar item and the page smoothly scrolls to that section + 4. OAuth listener and other page-level state survive scrolling and sidebar navigation without unmounting +**Plans**: 1 plan +Plans: +- [x] 09-01-PLAN.md — Single scrollable settings page with IntersectionObserver sidebar tracking +**UI hint**: yes + +## Progress + +| Phase | Milestone | Plans Complete | Status | Completed | +|-------|-----------|----------------|--------|-----------| +| 1. OCI Component Pull | v1.0 | 2/2 | Complete | 2026-03-24 | +| 2. WIT-to-Schema Tooling | v1.0 | 2/2 | Complete | 2026-03-25 | +| 3. MCP Execution Interface | v1.0 | 3/3 | Complete | 2026-03-25 | +| 4. Rust Event Foundation | v1.0 | 1/1 | Complete | 2026-04-07 | +| 5. Settings Decomposition | v1.0 | 2/2 | Complete | 2026-04-07 | +| 6. Unified Activity Frontend | v1.0 | 2/2 | Complete | 2026-04-07 | +| 7. Groq & OpenRouter Providers | v1.1 | 0/1 | In progress | - | +| 8. Ollama Provider | v1.1 | 0/1 | Not started | - | +| 9. Settings Scroll Refactor | v1.1 | 0/? | Not started | - | diff --git a/.planning/milestones/v1.1-phases/07-groq-openrouter-providers/07-01-PLAN.md b/.planning/milestones/v1.1-phases/07-groq-openrouter-providers/07-01-PLAN.md new file mode 100644 index 000000000..7b54c2386 --- /dev/null +++ b/.planning/milestones/v1.1-phases/07-groq-openrouter-providers/07-01-PLAN.md @@ -0,0 +1,322 @@ +--- +phase: 07-groq-openrouter-providers +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - app/src/components/settings/AgentSection.tsx + - app/agent/entrypoint.ts +autonomous: false +requirements: + - PROV-01 + - PROV-02 + - PROV-03 + +must_haves: + truths: + - "User can select Groq from the provider dropdown in agent settings" + - "User can select OpenRouter from the provider dropdown in agent settings" + - "User can enter and save an API key for Groq; key persists across app restarts" + - "User can enter and save an API key for OpenRouter; key persists across app restarts" + - "After saving Groq as provider and restarting, the agent sidecar uses Groq for responses" + - "After saving OpenRouter as provider and restarting, the agent sidecar uses OpenRouter for responses" + artifacts: + - path: "app/src/components/settings/AgentSection.tsx" + provides: "Groq and OpenRouter in provider dropdown, dynamic model placeholder" + contains: "option value=\"groq\"" + - path: "app/agent/entrypoint.ts" + provides: "Settings-aware startup model resolution" + contains: "agent_model_provider" + key_links: + - from: "app/src/components/settings/AgentSection.tsx" + to: "cmd_save_agent_settings" + via: "saveAgentSettings({ agent_model_provider: e.target.value })" + pattern: "saveAgentSettings.*agent_model_provider" + - from: "app/agent/entrypoint.ts" + to: "settings.json" + via: "readFileSync to load saved provider/model at startup" + pattern: "agent_model_provider" + - from: "app/agent/entrypoint.ts" + to: "modelRegistry.find" + via: "resolve saved provider+model to a Model object" + pattern: "modelRegistry\\.find" +--- + + +Add Groq and OpenRouter as selectable agent providers in the desktop app settings, with API key persistence and sidecar startup integration. + +Purpose: Users can choose open-source AI providers (Groq, OpenRouter) from the settings dropdown, enter API keys, and have the agent use those providers after restart -- enabling access to models like Llama 3.3 and OpenRouter's model catalog. + +Output: Two modified files -- AgentSection.tsx (UI dropdown + dynamic placeholder) and entrypoint.ts (settings-aware startup). + + + +@$HOME/.claude/get-shit-done/workflows/execute-plan.md +@$HOME/.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/07-groq-openrouter-providers/07-CONTEXT.md +@.planning/phases/07-groq-openrouter-providers/07-RESEARCH.md + +@app/src/components/settings/AgentSection.tsx +@app/agent/entrypoint.ts + + + + +From app/src/components/settings/AgentSection.tsx (current provider dropdown lines 203-218): +```tsx + +``` + +From app/agent/entrypoint.ts (current hard-coded default, line 49): +```ts +const defaultModel = getModel("anthropic", "claude-sonnet-4-20250514"); +``` + +From @mariozechner/pi-ai types.d.ts (KnownProvider union includes "groq" and "openrouter"): +```ts +type KnownProvider = "anthropic" | "google" | "groq" | "openai" | "openrouter" | ...; +``` + +From @mariozechner/pi-coding-agent model-registry.d.ts: +```ts +class ModelRegistry { + static inMemory(authStorage: AuthStorage): ModelRegistry; + find(provider: string, modelId: string): Model | undefined; +} +``` + +From app/agent/entrypoint.ts (createAgentSessionFromServices call, line 119): +```ts +return { + ...(await createAgentSessionFromServices({ + services, + sessionManager, + sessionStartEvent, + model: defaultModel ?? undefined, + thinkingLevel: "low", + tools: createCodingTools(runtimeCwd), + })), + services, + diagnostics: services.diagnostics, +}; +``` + + + + + + + Task 1: Add Groq and OpenRouter to AgentSection UI + app/src/components/settings/AgentSection.tsx + + - app/src/components/settings/AgentSection.tsx + + +Modify `app/src/components/settings/AgentSection.tsx` with three changes: + +1. Add a `DEFAULT_MODELS` constant near the top of the file (after the `OAUTH_PROVIDERS` line, before the `AgentSectionProps` interface). Exact content: + +```tsx +const DEFAULT_MODELS: Record = { + anthropic: 'claude-sonnet-4-20250514', + google: 'gemini-2.0-flash', + groq: 'llama-3.3-70b-versatile', + openai: 'gpt-4o', + openrouter: 'anthropic/claude-sonnet-4-20250514', +}; +``` + +2. Replace the three ` + + cd /workspace && grep -n 'option value="groq"' app/src/components/settings/AgentSection.tsx && grep -n 'option value="openrouter"' app/src/components/settings/AgentSection.tsx && grep -n 'DEFAULT_MODELS' app/src/components/settings/AgentSection.tsx && grep -n 'DEFAULT_MODELS\[settings.agent_model_provider' app/src/components/settings/AgentSection.tsx + + + - AgentSection.tsx contains `` + - AgentSection.tsx contains `` + - AgentSection.tsx contains `const DEFAULT_MODELS: Record` + - AgentSection.tsx contains `groq: 'llama-3.3-70b-versatile'` + - AgentSection.tsx contains `openrouter: 'anthropic/claude-sonnet-4-20250514'` + - AgentSection.tsx model input placeholder uses `DEFAULT_MODELS[settings.agent_model_provider` + - Options appear in alphabetical order: Anthropic, Google, Groq, OpenAI, OpenRouter + - `OAUTH_PROVIDERS` set does NOT contain 'groq' or 'openrouter' + - `just app-build-frontend` succeeds (TypeScript compiles) + + Provider dropdown shows 5 providers in alphabetical order; model placeholder updates dynamically when provider changes; frontend builds without errors. + + + + Task 2: Read saved settings at sidecar startup + app/agent/entrypoint.ts + + - app/agent/entrypoint.ts + + +Modify `app/agent/entrypoint.ts` to read `settings.json` at startup instead of hard-coding the Anthropic default. + +1. Add `readFileSync` to the existing `import { mkdirSync, existsSync } from "node:fs"` statement at line 88: + +```ts +import { mkdirSync, existsSync, readFileSync } from "node:fs"; +``` + +2. Replace the hard-coded default model line (line 49): + +```ts +const defaultModel = getModel("anthropic", "claude-sonnet-4-20250514"); +``` + +With settings-aware resolution: + +```ts +// Read saved provider/model from settings.json (lives in authDir alongside auth.json) +let savedProvider = "anthropic"; +let savedModelId = "claude-sonnet-4-20250514"; +try { + const settingsPath = path.join(authDir, "settings.json"); + if (existsSync(settingsPath)) { + const saved = JSON.parse(readFileSync(settingsPath, "utf-8")); + if (saved.agent_model_provider) savedProvider = saved.agent_model_provider; + if (saved.agent_model_id) savedModelId = saved.agent_model_id; + } +} catch { + // Use defaults on any read/parse error +} +const defaultModel = modelRegistry.find(savedProvider, savedModelId) + ?? getModel("anthropic", "claude-sonnet-4-20250514"); +``` + +Note: `existsSync` and `readFileSync` imports must be moved UP from line 88 to be available at line 49. The simplest approach: move the `import { mkdirSync, existsSync, readFileSync } from "node:fs"` to the top-level imports section (after line 29), and remove it from line 88. The `mkdirSync` usage at line 90-91 still works because it's after the import. + +Do NOT: +- Use `ModelRegistry.create()` with a modelsJsonPath (keep `inMemory()`) +- Add any async file reading (use synchronous `readFileSync` -- this runs once at startup) +- Remove the `getModel("anthropic", "claude-sonnet-4-20250514")` fallback + + + cd /workspace && grep -n 'agent_model_provider' app/agent/entrypoint.ts && grep -n 'readFileSync' app/agent/entrypoint.ts && grep -n 'modelRegistry.find' app/agent/entrypoint.ts && grep -n 'getModel.*anthropic.*claude-sonnet' app/agent/entrypoint.ts + + + - entrypoint.ts contains `readFileSync` import from "node:fs" + - entrypoint.ts contains `saved.agent_model_provider` (reads provider from settings) + - entrypoint.ts contains `saved.agent_model_id` (reads model ID from settings) + - entrypoint.ts contains `modelRegistry.find(savedProvider, savedModelId)` (resolves saved provider) + - entrypoint.ts contains `?? getModel("anthropic", "claude-sonnet-4-20250514")` (Anthropic fallback) + - entrypoint.ts contains `path.join(authDir, "settings.json")` (reads from correct directory) + - entrypoint.ts does NOT contain `ModelRegistry.create` (stays with inMemory) + + Sidecar reads settings.json at startup and uses saved provider/model; falls back to Anthropic if settings missing or model not found. + + + + Task 3: Verify provider selection and API key flow + app/src/components/settings/AgentSection.tsx, app/agent/entrypoint.ts + +Human verification of the complete Groq/OpenRouter provider flow. Run `just app-dev` and verify the UI and sidecar integration work end-to-end per the steps in how-to-verify. + + Groq and OpenRouter added as selectable agent providers with dynamic model placeholders and settings-aware sidecar startup. + + 1. Run `just app-dev` to start the app in dev mode + 2. Navigate to Settings + 3. In the AI Agent section, open the Provider dropdown + 4. Verify 5 options appear in this order: Anthropic, Google, Groq, OpenAI, OpenRouter + 5. Select "Groq" -- verify the model placeholder changes to "llama-3.3-70b-versatile" + 6. Select "OpenRouter" -- verify the model placeholder changes to "anthropic/claude-sonnet-4-20250514" + 7. Select "Anthropic" -- verify placeholder returns to "claude-sonnet-4-20250514" + 8. Select "Groq", enter a test API key (any string), click Save -- verify key shows as masked + 9. Click "Remove" to clean up the test key + + + cd /workspace && just app-build-frontend + + + - Provider dropdown shows 5 providers in alphabetical order + - Model placeholder updates when provider changes + - API key save/remove works for Groq and OpenRouter + + User confirms all provider selection, placeholder, and API key flows work correctly in the running app. + Type "approved" or describe issues + + + + + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| Settings UI -> Tauri IPC -> settings.json | User input (provider string, model ID) crosses from untrusted frontend to disk | +| settings.json -> entrypoint.ts | File on disk read at sidecar startup -- could be tampered | +| API key input -> auth.json | Secrets cross from UI to encrypted-at-rest storage | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Disposition | Mitigation Plan | +|-----------|----------|-----------|-------------|-----------------| +| T-07-01 | Tampering | settings.json read in entrypoint.ts | accept | settings.json is in app_config_dir with user-level permissions; tampering requires local access which already implies full compromise. JSON.parse in try/catch prevents crash on malformed data. | +| T-07-02 | Information Disclosure | API keys in auth.json | mitigate | Existing mitigation: auth.json written with 0600 permissions on Unix (commands.rs:1500-1508). AgentApiKeyField masks display. No change needed -- reusing existing secure path. | +| T-07-03 | Spoofing | Provider string injection via dropdown | accept | Provider value comes from ` + + +1. `just app-build-frontend` succeeds -- TypeScript compiles without errors +2. `grep -c 'option value=' app/src/components/settings/AgentSection.tsx` returns 5 (five provider options) +3. `grep 'DEFAULT_MODELS' app/src/components/settings/AgentSection.tsx` shows the constant exists +4. `grep 'modelRegistry.find' app/agent/entrypoint.ts` shows settings-aware model resolution +5. `grep 'getModel.*anthropic' app/agent/entrypoint.ts` shows Anthropic fallback preserved + + + +- Provider dropdown shows Anthropic, Google, Groq, OpenAI, OpenRouter in alphabetical order +- Model placeholder updates dynamically when provider changes +- API key entry works for Groq and OpenRouter (via existing AgentApiKeyField) +- Sidecar startup reads settings.json and uses saved provider/model +- Sidecar falls back to Anthropic if settings.json missing or model not found +- Frontend builds without TypeScript errors + + + +After completion, create `.planning/phases/07-groq-openrouter-providers/07-01-SUMMARY.md` + diff --git a/.planning/milestones/v1.1-phases/07-groq-openrouter-providers/07-01-SUMMARY.md b/.planning/milestones/v1.1-phases/07-groq-openrouter-providers/07-01-SUMMARY.md new file mode 100644 index 000000000..4d0100732 --- /dev/null +++ b/.planning/milestones/v1.1-phases/07-groq-openrouter-providers/07-01-SUMMARY.md @@ -0,0 +1,120 @@ +--- +phase: 07-groq-openrouter-providers +plan: 01 +subsystem: desktop-app +tags: [frontend, agent, providers, groq, openrouter, settings] +dependency_graph: + requires: [] + provides: [groq-provider-ui, openrouter-provider-ui, settings-aware-sidecar-startup] + affects: [app/src/components/settings/AgentSection.tsx, app/agent/entrypoint.ts] +tech_stack: + added: [] + patterns: [DEFAULT_MODELS record for dynamic placeholders, settings.json read at sidecar startup] +key_files: + created: [] + modified: + - app/src/components/settings/AgentSection.tsx + - app/agent/entrypoint.ts +decisions: + - Used DEFAULT_MODELS record for dynamic placeholder resolution instead of switch/if-else + - Moved fs imports to top-level to ensure availability before authDir usage + - Used synchronous readFileSync at startup (not async) for simplicity + - Kept ModelRegistry.inMemory() — no models.json needed for Groq/OpenRouter +requirements-completed: [PROV-01, PROV-02, PROV-03] +metrics: + duration: ~30min + completed: "2026-04-08" + tasks_completed: 3 + tasks_total: 3 + files_changed: 2 +--- + +# Phase 7 Plan 01: Groq and OpenRouter Providers Summary + +**Groq and OpenRouter added as selectable agent providers with dynamic model placeholders and settings-aware sidecar startup via settings.json read at startup.** + +## Performance + +- **Duration:** ~30 min +- **Started:** 2026-04-08T12:00:00Z +- **Completed:** 2026-04-08T12:30:00Z +- **Tasks:** 3 of 3 (including human-verify checkpoint) +- **Files modified:** 2 + +## Accomplishments +- Provider dropdown now shows 5 providers in alphabetical order: Anthropic, Google, Groq, OpenAI, OpenRouter +- Dynamic model placeholder updates to provider-appropriate default when user changes selection +- Agent sidecar reads settings.json at startup and uses saved provider/model (falls back to Anthropic) +- Human verification checkpoint approved by user confirming UI and flow work correctly + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Add Groq and OpenRouter to AgentSection UI** - `ca2ef451` (feat) +2. **Task 2: Read saved settings at sidecar startup** - `3ab2b956` (feat) +3. **Task 3: Verify provider selection and API key flow** - Human-verified and approved + +**Plan metadata:** `48ea9ee6` (docs: complete plan summary) + +## Files Created/Modified +- `app/src/components/settings/AgentSection.tsx` - Added DEFAULT_MODELS constant, Groq/OpenRouter options in dropdown, dynamic model placeholder +- `app/agent/entrypoint.ts` - Settings-aware model resolution at startup using readFileSync + modelRegistry.find() + +## Decisions Made +- Used DEFAULT_MODELS record for dynamic placeholder resolution instead of switch/if-else (cleaner, extensible) +- Moved fs imports to top-level to ensure availability before authDir usage at line 49 +- Used synchronous readFileSync at startup (not async) for simplicity — runs once, no blocking concern +- Kept ModelRegistry.inMemory() — no models.json needed for Groq/OpenRouter (they use API keys, not local model files) + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered + +None. + +## Verification Results + +- TypeScript compilation (`npx tsc --noEmit`): PASSED — no errors +- `option value="groq"` present in AgentSection.tsx: CONFIRMED +- `option value="openrouter"` present in AgentSection.tsx: CONFIRMED +- `DEFAULT_MODELS` constant present: CONFIRMED +- `DEFAULT_MODELS[settings.agent_model_provider` placeholder: CONFIRMED +- `readFileSync` imported from "node:fs": CONFIRMED +- `saved.agent_model_provider` read from settings: CONFIRMED +- `modelRegistry.find(savedProvider, savedModelId)` resolution: CONFIRMED +- `?? getModel("anthropic", "claude-sonnet-4-20250514")` fallback: CONFIRMED +- `ModelRegistry.create` NOT used (stays with inMemory): CONFIRMED +- `OAUTH_PROVIDERS` does NOT contain 'groq' or 'openrouter': CONFIRMED +- Human verification (Task 3): APPROVED by user + +## User Setup Required + +None - no external service configuration required. Users enter API keys directly in the app Settings UI. + +## Next Phase Readiness + +- Groq and OpenRouter provider UI fully integrated; ready for use +- Settings persistence (save/load provider + model + API key) is complete +- Future phases can add additional providers by extending the `DEFAULT_MODELS` record and the `